@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.
- package/COMMERCIAL_LICENSE.txt +34 -0
- package/LICENSE +661 -0
- package/README.md +707 -0
- package/dist/application/base.command.d.ts +44 -0
- package/dist/application/base.command.d.ts.map +1 -0
- package/dist/application/base.command.js +58 -0
- package/dist/application/base.command.js.map +1 -0
- package/dist/application/base.query.d.ts +42 -0
- package/dist/application/base.query.d.ts.map +1 -0
- package/dist/application/base.query.js +52 -0
- package/dist/application/base.query.js.map +1 -0
- package/dist/application/command-base.handler.d.ts +106 -0
- package/dist/application/command-base.handler.d.ts.map +1 -0
- package/dist/application/command-base.handler.js +119 -0
- package/dist/application/command-base.handler.js.map +1 -0
- package/dist/application/index.d.ts +16 -0
- package/dist/application/index.d.ts.map +1 -0
- package/dist/application/index.js +33 -0
- package/dist/application/index.js.map +1 -0
- package/dist/application/ports/cache.port.d.ts +104 -0
- package/dist/application/ports/cache.port.d.ts.map +1 -0
- package/dist/application/ports/cache.port.js +17 -0
- package/dist/application/ports/cache.port.js.map +1 -0
- package/dist/application/ports/command-repository.port.d.ts +28 -0
- package/dist/application/ports/command-repository.port.d.ts.map +1 -0
- package/dist/application/ports/command-repository.port.js +4 -0
- package/dist/application/ports/command-repository.port.js.map +1 -0
- package/dist/application/ports/domain-event-publisher.port.d.ts +18 -0
- package/dist/application/ports/domain-event-publisher.port.d.ts.map +1 -0
- package/dist/application/ports/domain-event-publisher.port.js +4 -0
- package/dist/application/ports/domain-event-publisher.port.js.map +1 -0
- package/dist/application/ports/query-repository.port.d.ts +26 -0
- package/dist/application/ports/query-repository.port.d.ts.map +1 -0
- package/dist/application/ports/query-repository.port.js +4 -0
- package/dist/application/ports/query-repository.port.js.map +1 -0
- package/dist/application/ports/write-side-aggregate-repository.port.d.ts +33 -0
- package/dist/application/ports/write-side-aggregate-repository.port.d.ts.map +1 -0
- package/dist/application/ports/write-side-aggregate-repository.port.js +4 -0
- package/dist/application/ports/write-side-aggregate-repository.port.js.map +1 -0
- package/dist/application/query.options.d.ts +22 -0
- package/dist/application/query.options.d.ts.map +1 -0
- package/dist/application/query.options.js +4 -0
- package/dist/application/query.options.js.map +1 -0
- package/dist/application/request-fields.helper.d.ts +5 -0
- package/dist/application/request-fields.helper.d.ts.map +1 -0
- package/dist/application/request-fields.helper.js +20 -0
- package/dist/application/request-fields.helper.js.map +1 -0
- package/dist/application/tenant-resolver.d.ts +56 -0
- package/dist/application/tenant-resolver.d.ts.map +1 -0
- package/dist/application/tenant-resolver.js +60 -0
- package/dist/application/tenant-resolver.js.map +1 -0
- package/dist/domain/decorators/apply-mutation.decorator.d.ts +64 -0
- package/dist/domain/decorators/apply-mutation.decorator.d.ts.map +1 -0
- package/dist/domain/decorators/apply-mutation.decorator.js +86 -0
- package/dist/domain/decorators/apply-mutation.decorator.js.map +1 -0
- package/dist/domain/decorators/mutable.decorator.d.ts +54 -0
- package/dist/domain/decorators/mutable.decorator.d.ts.map +1 -0
- package/dist/domain/decorators/mutable.decorator.js +76 -0
- package/dist/domain/decorators/mutable.decorator.js.map +1 -0
- package/dist/domain/events/domain.event.d.ts +13 -0
- package/dist/domain/events/domain.event.d.ts.map +1 -0
- package/dist/domain/events/domain.event.js +20 -0
- package/dist/domain/events/domain.event.js.map +1 -0
- package/dist/domain/events/event.interface.d.ts +9 -0
- package/dist/domain/events/event.interface.d.ts.map +1 -0
- package/dist/domain/events/event.interface.js +4 -0
- package/dist/domain/events/event.interface.js.map +1 -0
- package/dist/domain/events/root-domain.event.d.ts +50 -0
- package/dist/domain/events/root-domain.event.d.ts.map +1 -0
- package/dist/domain/events/root-domain.event.js +156 -0
- package/dist/domain/events/root-domain.event.js.map +1 -0
- package/dist/domain/exceptions/concurrency-conflict.error.d.ts +20 -0
- package/dist/domain/exceptions/concurrency-conflict.error.d.ts.map +1 -0
- package/dist/domain/exceptions/concurrency-conflict.error.js +32 -0
- package/dist/domain/exceptions/concurrency-conflict.error.js.map +1 -0
- package/dist/domain/exceptions/domain.exception.d.ts +30 -0
- package/dist/domain/exceptions/domain.exception.d.ts.map +1 -0
- package/dist/domain/exceptions/domain.exception.js +39 -0
- package/dist/domain/exceptions/domain.exception.js.map +1 -0
- package/dist/domain/exceptions/entity-not-found.exception.d.ts +14 -0
- package/dist/domain/exceptions/entity-not-found.exception.d.ts.map +1 -0
- package/dist/domain/exceptions/entity-not-found.exception.js +23 -0
- package/dist/domain/exceptions/entity-not-found.exception.js.map +1 -0
- package/dist/domain/exceptions/invalid-value.exception.d.ts +35 -0
- package/dist/domain/exceptions/invalid-value.exception.d.ts.map +1 -0
- package/dist/domain/exceptions/invalid-value.exception.js +67 -0
- package/dist/domain/exceptions/invalid-value.exception.js.map +1 -0
- package/dist/domain/exceptions/missing-tenant-context.exception.d.ts +24 -0
- package/dist/domain/exceptions/missing-tenant-context.exception.d.ts.map +1 -0
- package/dist/domain/exceptions/missing-tenant-context.exception.js +33 -0
- package/dist/domain/exceptions/missing-tenant-context.exception.js.map +1 -0
- package/dist/domain/exceptions/transient-operation.error.d.ts +17 -0
- package/dist/domain/exceptions/transient-operation.error.d.ts.map +1 -0
- package/dist/domain/exceptions/transient-operation.error.js +27 -0
- package/dist/domain/exceptions/transient-operation.error.js.map +1 -0
- package/dist/domain/exceptions/unknown-mutable-field.error.d.ts +14 -0
- package/dist/domain/exceptions/unknown-mutable-field.error.d.ts.map +1 -0
- package/dist/domain/exceptions/unknown-mutable-field.error.js +24 -0
- package/dist/domain/exceptions/unknown-mutable-field.error.js.map +1 -0
- package/dist/domain/index.d.ts +26 -0
- package/dist/domain/index.d.ts.map +1 -0
- package/dist/domain/index.js +43 -0
- package/dist/domain/index.js.map +1 -0
- package/dist/domain/interfaces/root-entity-snapshot.interface.d.ts +7 -0
- package/dist/domain/interfaces/root-entity-snapshot.interface.d.ts.map +1 -0
- package/dist/domain/interfaces/root-entity-snapshot.interface.js +4 -0
- package/dist/domain/interfaces/root-entity-snapshot.interface.js.map +1 -0
- package/dist/domain/models/aggregate-root.d.ts +87 -0
- package/dist/domain/models/aggregate-root.d.ts.map +1 -0
- package/dist/domain/models/aggregate-root.js +122 -0
- package/dist/domain/models/aggregate-root.js.map +1 -0
- package/dist/domain/models/root.entity.d.ts +208 -0
- package/dist/domain/models/root.entity.d.ts.map +1 -0
- package/dist/domain/models/root.entity.js +350 -0
- package/dist/domain/models/root.entity.js.map +1 -0
- package/dist/domain/rules/number.rule.d.ts +81 -0
- package/dist/domain/rules/number.rule.d.ts.map +1 -0
- package/dist/domain/rules/number.rule.js +81 -0
- package/dist/domain/rules/number.rule.js.map +1 -0
- package/dist/domain/rules/rule-support.d.ts +6 -0
- package/dist/domain/rules/rule-support.d.ts.map +1 -0
- package/dist/domain/rules/rule-support.js +19 -0
- package/dist/domain/rules/rule-support.js.map +1 -0
- package/dist/domain/rules/text.rule.d.ts +98 -0
- package/dist/domain/rules/text.rule.d.ts.map +1 -0
- package/dist/domain/rules/text.rule.js +103 -0
- package/dist/domain/rules/text.rule.js.map +1 -0
- package/dist/domain/rules/value-violation.d.ts +44 -0
- package/dist/domain/rules/value-violation.d.ts.map +1 -0
- package/dist/domain/rules/value-violation.js +4 -0
- package/dist/domain/rules/value-violation.js.map +1 -0
- package/dist/http/domain-error-http-status.d.ts +43 -0
- package/dist/http/domain-error-http-status.d.ts.map +1 -0
- package/dist/http/domain-error-http-status.js +27 -0
- package/dist/http/domain-error-http-status.js.map +1 -0
- package/dist/http/index.d.ts +9 -0
- package/dist/http/index.d.ts.map +1 -0
- package/dist/http/index.js +26 -0
- package/dist/http/index.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/persistence/cache/cache-logger.d.ts +29 -0
- package/dist/persistence/cache/cache-logger.d.ts.map +1 -0
- package/dist/persistence/cache/cache-logger.js +23 -0
- package/dist/persistence/cache/cache-logger.js.map +1 -0
- package/dist/persistence/cache/memory.cache.d.ts +72 -0
- package/dist/persistence/cache/memory.cache.d.ts.map +1 -0
- package/dist/persistence/cache/memory.cache.js +183 -0
- package/dist/persistence/cache/memory.cache.js.map +1 -0
- package/dist/persistence/command-repository.abstract.d.ts +39 -0
- package/dist/persistence/command-repository.abstract.d.ts.map +1 -0
- package/dist/persistence/command-repository.abstract.js +43 -0
- package/dist/persistence/command-repository.abstract.js.map +1 -0
- package/dist/persistence/decorators/acknowledge-persisted.decorator.d.ts +82 -0
- package/dist/persistence/decorators/acknowledge-persisted.decorator.d.ts.map +1 -0
- package/dist/persistence/decorators/acknowledge-persisted.decorator.js +82 -0
- package/dist/persistence/decorators/acknowledge-persisted.decorator.js.map +1 -0
- package/dist/persistence/decorators/cache.decorator.d.ts +93 -0
- package/dist/persistence/decorators/cache.decorator.d.ts.map +1 -0
- package/dist/persistence/decorators/cache.decorator.js +200 -0
- package/dist/persistence/decorators/cache.decorator.js.map +1 -0
- package/dist/persistence/decorators/from-cache.decorator.d.ts +65 -0
- package/dist/persistence/decorators/from-cache.decorator.d.ts.map +1 -0
- package/dist/persistence/decorators/from-cache.decorator.js +165 -0
- package/dist/persistence/decorators/from-cache.decorator.js.map +1 -0
- package/dist/persistence/decorators/map-persistence-errors.decorator.d.ts +88 -0
- package/dist/persistence/decorators/map-persistence-errors.decorator.d.ts.map +1 -0
- package/dist/persistence/decorators/map-persistence-errors.decorator.js +89 -0
- package/dist/persistence/decorators/map-persistence-errors.decorator.js.map +1 -0
- package/dist/persistence/decorators/persisted-write.decorator.d.ts +61 -0
- package/dist/persistence/decorators/persisted-write.decorator.d.ts.map +1 -0
- package/dist/persistence/decorators/persisted-write.decorator.js +57 -0
- package/dist/persistence/decorators/persisted-write.decorator.js.map +1 -0
- package/dist/persistence/helpers/cache-barrier.helper.d.ts +70 -0
- package/dist/persistence/helpers/cache-barrier.helper.d.ts.map +1 -0
- package/dist/persistence/helpers/cache-barrier.helper.js +57 -0
- package/dist/persistence/helpers/cache-barrier.helper.js.map +1 -0
- package/dist/persistence/helpers/cache-key.helper.d.ts +73 -0
- package/dist/persistence/helpers/cache-key.helper.d.ts.map +1 -0
- package/dist/persistence/helpers/cache-key.helper.js +132 -0
- package/dist/persistence/helpers/cache-key.helper.js.map +1 -0
- package/dist/persistence/helpers/cache-owner.helper.d.ts +9 -0
- package/dist/persistence/helpers/cache-owner.helper.d.ts.map +1 -0
- package/dist/persistence/helpers/cache-owner.helper.js +22 -0
- package/dist/persistence/helpers/cache-owner.helper.js.map +1 -0
- package/dist/persistence/helpers/cache-snapshot.helper.d.ts +28 -0
- package/dist/persistence/helpers/cache-snapshot.helper.d.ts.map +1 -0
- package/dist/persistence/helpers/cache-snapshot.helper.js +42 -0
- package/dist/persistence/helpers/cache-snapshot.helper.js.map +1 -0
- package/dist/persistence/helpers/cache-version.helper.d.ts +29 -0
- package/dist/persistence/helpers/cache-version.helper.d.ts.map +1 -0
- package/dist/persistence/helpers/cache-version.helper.js +71 -0
- package/dist/persistence/helpers/cache-version.helper.js.map +1 -0
- package/dist/persistence/index.d.ts +23 -0
- package/dist/persistence/index.d.ts.map +1 -0
- package/dist/persistence/index.js +40 -0
- package/dist/persistence/index.js.map +1 -0
- package/dist/persistence/persistence-dialect.d.ts +35 -0
- package/dist/persistence/persistence-dialect.d.ts.map +1 -0
- package/dist/persistence/persistence-dialect.js +31 -0
- package/dist/persistence/persistence-dialect.js.map +1 -0
- package/dist/persistence/query-repository.abstract.d.ts +76 -0
- package/dist/persistence/query-repository.abstract.d.ts.map +1 -0
- package/dist/persistence/query-repository.abstract.js +67 -0
- package/dist/persistence/query-repository.abstract.js.map +1 -0
- package/dist/types/Method.type.d.ts +2 -0
- package/dist/types/Method.type.d.ts.map +1 -0
- package/dist/types/Method.type.js +4 -0
- package/dist/types/Method.type.js.map +1 -0
- package/package.json +80 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contract for write-side command repositories.
|
|
3
|
+
*
|
|
4
|
+
* Persists an entity or aggregate state into persistence storage.
|
|
5
|
+
*
|
|
6
|
+
* @typeParam TEntity - The aggregate entity or payload being persisted.
|
|
7
|
+
* @typeParam TResult - The result type returned after persistence (or null on deletion).
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* constructor(
|
|
12
|
+
* @Inject(COMMAND_REPOSITORY.createUser)
|
|
13
|
+
* private readonly users: ICommandRepository<User, UserSnapshot>,
|
|
14
|
+
* ) {}
|
|
15
|
+
*
|
|
16
|
+
* await this.users.save(user);
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export interface ICommandRepository<TEntity = unknown, TResult = unknown> {
|
|
20
|
+
/**
|
|
21
|
+
* Persists the aggregate/payload.
|
|
22
|
+
*
|
|
23
|
+
* Returning `null` is the conventional deletion result used by the DDD cache
|
|
24
|
+
* decorator to trigger eviction/barrier behavior.
|
|
25
|
+
*/
|
|
26
|
+
save(entity: TEntity): Promise<TResult | null>;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=command-repository.port.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command-repository.port.d.ts","sourceRoot":"","sources":["../../../application/ports/command-repository.port.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,kBAAkB,CAAC,OAAO,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO;IACtE;;;;;OAKG;IACH,IAAI,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;CAChD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command-repository.port.js","sourceRoot":"","sources":["../../../application/ports/command-repository.port.ts"],"names":[],"mappings":";AAAA,sEAAsE"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { IEvent } from '../../domain/events/event.interface';
|
|
2
|
+
/**
|
|
3
|
+
* Publishes the buffered domain events of an aggregate after a command succeeds.
|
|
4
|
+
*
|
|
5
|
+
* Any bus with a `publishAll(events)` method fits, including the NestJS CQRS
|
|
6
|
+
* `EventBus`, so a Nest handler passes its injected `EventBus` unchanged.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* const publisher: IDomainEventPublisher = {
|
|
11
|
+
* publishAll: (events) => events.forEach((event) => emitter.emit('event', event)),
|
|
12
|
+
* };
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export interface IDomainEventPublisher {
|
|
16
|
+
publishAll(events: IEvent[]): unknown;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=domain-event-publisher.port.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domain-event-publisher.port.d.ts","sourceRoot":"","sources":["../../../application/ports/domain-event-publisher.port.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qCAAqC,CAAC;AAElE;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,qBAAqB;IACpC,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;CACvC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domain-event-publisher.port.js","sourceRoot":"","sources":["../../../application/ports/domain-event-publisher.port.ts"],"names":[],"mappings":";AAAA,sEAAsE"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { IQueryOptions } from '../query.options';
|
|
2
|
+
/**
|
|
3
|
+
* Application-facing read repository contract.
|
|
4
|
+
*
|
|
5
|
+
* Query handlers depend on this interface rather than ORM/query-builder
|
|
6
|
+
* implementations. Repository adapters may use {@link FromCache} internally,
|
|
7
|
+
* but callers receive only the declared `TResult`.
|
|
8
|
+
*
|
|
9
|
+
* @typeParam TQuery - Query object accepted by {@link find}.
|
|
10
|
+
* @typeParam TResult - Domain/read result returned to the application layer.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* constructor(
|
|
15
|
+
* @Inject(QUERY_REPOSITORY.getUser)
|
|
16
|
+
* private readonly users: IQueryRepository<GetUserQuery, User | null>,
|
|
17
|
+
* ) {}
|
|
18
|
+
*
|
|
19
|
+
* const user = await this.users.find(query);
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export interface IQueryRepository<TQuery = IQueryOptions, TResult = unknown> {
|
|
23
|
+
/** Resolves one application query. */
|
|
24
|
+
find(query: TQuery): Promise<TResult>;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=query-repository.port.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-repository.port.d.ts","sourceRoot":"","sources":["../../../application/ports/query-repository.port.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,gBAAgB,CAAC,MAAM,GAAG,aAAa,EAAE,OAAO,GAAG,OAAO;IACzE,sCAAsC;IACtC,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACvC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-repository.port.js","sourceRoot":"","sources":["../../../application/ports/query-repository.port.ts"],"names":[],"mappings":";AAAA,sEAAsE"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ICommandRepository } from './command-repository.port';
|
|
2
|
+
/**
|
|
3
|
+
* Write-side repository contract for commands that mutate an existing aggregate.
|
|
4
|
+
*
|
|
5
|
+
* `findById()` must read authoritative persistence and must not use a read-side
|
|
6
|
+
* result cache. Adapters backed by an ORM identity map should force a refresh so
|
|
7
|
+
* optimistic-concurrency checks are based on current persisted state.
|
|
8
|
+
*
|
|
9
|
+
* Use this contract in update/delete command handlers that must authorize and
|
|
10
|
+
* mutate the real aggregate before saving it.
|
|
11
|
+
*
|
|
12
|
+
* @typeParam TEntity - Rehydrated aggregate type.
|
|
13
|
+
* @typeParam TId - Aggregate identifier type.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* const user = await repository.findById(command.id);
|
|
18
|
+
* if (!user) throw new EntityNotFoundException('User', command.id);
|
|
19
|
+
* authorizer.authorize('update', user);
|
|
20
|
+
* user.update({ department: command.department });
|
|
21
|
+
* await repository.save(user);
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export interface IWriteSideAggregateRepository<TEntity, TId = string> extends ICommandRepository<TEntity, unknown> {
|
|
25
|
+
/**
|
|
26
|
+
* Loads the current authoritative aggregate for a mutating command.
|
|
27
|
+
*
|
|
28
|
+
* @param id - Aggregate identifier.
|
|
29
|
+
* @returns The rehydrated aggregate, or `null` when it does not exist.
|
|
30
|
+
*/
|
|
31
|
+
findById(id: TId): Promise<TEntity | null>;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=write-side-aggregate-repository.port.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-side-aggregate-repository.port.d.ts","sourceRoot":"","sources":["../../../application/ports/write-side-aggregate-repository.port.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAEpE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,WAAW,6BAA6B,CAAC,OAAO,EAAE,GAAG,GAAG,MAAM,CAClE,SAAQ,kBAAkB,CAAC,OAAO,EAAE,OAAO,CAAC;IAC5C;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,EAAE,GAAG,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;CAC5C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-side-aggregate-repository.port.js","sourceRoot":"","sources":["../../../application/ports/write-side-aggregate-repository.port.ts"],"names":[],"mappings":";AAAA,sEAAsE"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metadata understood by DDD query repositories and read-through cache helpers.
|
|
3
|
+
*
|
|
4
|
+
* Query implementations normally inherit this through {@link BaseQuery}.
|
|
5
|
+
*/
|
|
6
|
+
export interface IQueryOptions {
|
|
7
|
+
/**
|
|
8
|
+
* Query metadata for repositories that read it. {@link FromCache} does not:
|
|
9
|
+
* it rehydrates every hit whenever a hydrator applies.
|
|
10
|
+
*
|
|
11
|
+
* @default false
|
|
12
|
+
*/
|
|
13
|
+
hydrate?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Bypass read-through cache lookup and fill, forcing an authoritative read
|
|
16
|
+
* directly from underlying persistence.
|
|
17
|
+
*
|
|
18
|
+
* @default false
|
|
19
|
+
*/
|
|
20
|
+
refresh?: boolean;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=query.options.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.options.d.ts","sourceRoot":"","sources":["../../application/query.options.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.options.js","sourceRoot":"","sources":["../../application/query.options.ts"],"names":[],"mappings":";AAAA,sEAAsE"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Defines a non-enumerable property, excluded from `toJSON()` and payload spreads. */
|
|
2
|
+
export declare function defineHidden(target: object, key: string, value: unknown): void;
|
|
3
|
+
/** Returns the enumerable own fields of a request whose value is not `undefined`. */
|
|
4
|
+
export declare function definedFields(request: object): Record<string, unknown>;
|
|
5
|
+
//# sourceMappingURL=request-fields.helper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-fields.helper.d.ts","sourceRoot":"","sources":["../../application/request-fields.helper.ts"],"names":[],"mappings":"AAEA,uFAAuF;AACvF,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,OAAO,GACb,IAAI,CAEN;AAED,qFAAqF;AACrF,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQtE"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* Copyright (C) 2026-present Aristotelis — see repository license. */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.defineHidden = defineHidden;
|
|
5
|
+
exports.definedFields = definedFields;
|
|
6
|
+
/** Defines a non-enumerable property, excluded from `toJSON()` and payload spreads. */
|
|
7
|
+
function defineHidden(target, key, value) {
|
|
8
|
+
Object.defineProperty(target, key, { value, enumerable: false });
|
|
9
|
+
}
|
|
10
|
+
/** Returns the enumerable own fields of a request whose value is not `undefined`. */
|
|
11
|
+
function definedFields(request) {
|
|
12
|
+
const json = {};
|
|
13
|
+
for (const [key, value] of Object.entries(request)) {
|
|
14
|
+
if (value !== undefined) {
|
|
15
|
+
json[key] = value;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return json;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=request-fields.helper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-fields.helper.js","sourceRoot":"","sources":["../../application/request-fields.helper.ts"],"names":[],"mappings":";AAAA,sEAAsE;;AAGtE,oCAMC;AAGD,sCAQC;AAlBD,uFAAuF;AACvF,SAAgB,YAAY,CAC1B,MAAc,EACd,GAAW,EACX,KAAc;IAEd,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC;AACnE,CAAC;AAED,qFAAqF;AACrF,SAAgB,aAAa,CAAC,OAAe;IAC3C,MAAM,IAAI,GAA4B,EAAE,CAAC;IACzC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACnD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACpB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An explicit tenant: a tenant id string, or an object carrying `tenantId`, such
|
|
3
|
+
* as a request or job context.
|
|
4
|
+
*/
|
|
5
|
+
export type TenantSource = string | {
|
|
6
|
+
readonly tenantId?: string | undefined;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Returns the tenant of the running unit of work, or `undefined` when there is
|
|
10
|
+
* none.
|
|
11
|
+
*/
|
|
12
|
+
export type TenantResolver = () => string | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* Registers where tenant-scoped helpers such as `cacheKey` and
|
|
15
|
+
* `cacheKeyTemplate` find the tenant when none is passed to them, typically the
|
|
16
|
+
* request or job context the application already keeps.
|
|
17
|
+
*
|
|
18
|
+
* Register it once at startup; a later call replaces it, and `undefined` removes
|
|
19
|
+
* it. When the resolver returns nothing, or none is registered, those helpers
|
|
20
|
+
* fail closed with {@link MissingTenantContextError}.
|
|
21
|
+
*
|
|
22
|
+
* @param resolver - Returns the current tenant, or `undefined` for none.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* const requestTenant = new AsyncLocalStorage<string>();
|
|
27
|
+
* setTenantResolver(() => requestTenant.getStore());
|
|
28
|
+
*
|
|
29
|
+
* // Per request or job:
|
|
30
|
+
* requestTenant.run(tenantId, () => handle(request));
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export declare function setTenantResolver(resolver: TenantResolver | undefined): void;
|
|
34
|
+
/**
|
|
35
|
+
* Returns the tenant for a security-sensitive operation, or fails closed.
|
|
36
|
+
*
|
|
37
|
+
* An explicit string always wins, even an empty one. An object uses its
|
|
38
|
+
* `tenantId`; without one, or when `source` is omitted, the tenant of the
|
|
39
|
+
* registered {@link setTenantResolver} resolver applies. Nothing resolved, or an
|
|
40
|
+
* empty tenant, throws {@link MissingTenantContextError}: keys for caches,
|
|
41
|
+
* idempotency and rate limits must never collapse several tenants into one
|
|
42
|
+
* shared namespace.
|
|
43
|
+
*
|
|
44
|
+
* @param source - The explicit tenant, or `undefined` to use the resolver.
|
|
45
|
+
* @param purpose - What the tenant is for, named in the error.
|
|
46
|
+
* @returns The non-empty tenant id.
|
|
47
|
+
* @throws {MissingTenantContextError} When no tenant can be resolved.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* // A key factory that receives a request or job context:
|
|
52
|
+
* const tenantId = requireTenantId(ctx, 'users.create idempotency key');
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export declare function requireTenantId(source: TenantSource | undefined, purpose: string): string;
|
|
56
|
+
//# sourceMappingURL=tenant-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-resolver.d.ts","sourceRoot":"","sources":["../../application/tenant-resolver.ts"],"names":[],"mappings":"AAIA;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAE/E;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,MAAM,GAAG,SAAS,CAAC;AAItD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,cAAc,GAAG,SAAS,GAAG,IAAI,CAE5E;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,YAAY,GAAG,SAAS,EAChC,OAAO,EAAE,MAAM,GACd,MAAM,CAOR"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* Copyright (C) 2026-present Aristotelis — see repository license. */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.setTenantResolver = setTenantResolver;
|
|
5
|
+
exports.requireTenantId = requireTenantId;
|
|
6
|
+
const missing_tenant_context_exception_1 = require("../domain/exceptions/missing-tenant-context.exception");
|
|
7
|
+
let tenantResolver;
|
|
8
|
+
/**
|
|
9
|
+
* Registers where tenant-scoped helpers such as `cacheKey` and
|
|
10
|
+
* `cacheKeyTemplate` find the tenant when none is passed to them, typically the
|
|
11
|
+
* request or job context the application already keeps.
|
|
12
|
+
*
|
|
13
|
+
* Register it once at startup; a later call replaces it, and `undefined` removes
|
|
14
|
+
* it. When the resolver returns nothing, or none is registered, those helpers
|
|
15
|
+
* fail closed with {@link MissingTenantContextError}.
|
|
16
|
+
*
|
|
17
|
+
* @param resolver - Returns the current tenant, or `undefined` for none.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* const requestTenant = new AsyncLocalStorage<string>();
|
|
22
|
+
* setTenantResolver(() => requestTenant.getStore());
|
|
23
|
+
*
|
|
24
|
+
* // Per request or job:
|
|
25
|
+
* requestTenant.run(tenantId, () => handle(request));
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
function setTenantResolver(resolver) {
|
|
29
|
+
tenantResolver = resolver;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Returns the tenant for a security-sensitive operation, or fails closed.
|
|
33
|
+
*
|
|
34
|
+
* An explicit string always wins, even an empty one. An object uses its
|
|
35
|
+
* `tenantId`; without one, or when `source` is omitted, the tenant of the
|
|
36
|
+
* registered {@link setTenantResolver} resolver applies. Nothing resolved, or an
|
|
37
|
+
* empty tenant, throws {@link MissingTenantContextError}: keys for caches,
|
|
38
|
+
* idempotency and rate limits must never collapse several tenants into one
|
|
39
|
+
* shared namespace.
|
|
40
|
+
*
|
|
41
|
+
* @param source - The explicit tenant, or `undefined` to use the resolver.
|
|
42
|
+
* @param purpose - What the tenant is for, named in the error.
|
|
43
|
+
* @returns The non-empty tenant id.
|
|
44
|
+
* @throws {MissingTenantContextError} When no tenant can be resolved.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* // A key factory that receives a request or job context:
|
|
49
|
+
* const tenantId = requireTenantId(ctx, 'users.create idempotency key');
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
function requireTenantId(source, purpose) {
|
|
53
|
+
const tenantId = typeof source === 'string'
|
|
54
|
+
? source
|
|
55
|
+
: (source?.tenantId ?? tenantResolver?.());
|
|
56
|
+
if (!tenantId)
|
|
57
|
+
throw new missing_tenant_context_exception_1.MissingTenantContextError(purpose);
|
|
58
|
+
return tenantId;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=tenant-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-resolver.js","sourceRoot":"","sources":["../../application/tenant-resolver.ts"],"names":[],"mappings":";AAAA,sEAAsE;;AAsCtE,8CAEC;AAuBD,0CAUC;AAvED,4GAAkG;AAclG,IAAI,cAA0C,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,SAAgB,iBAAiB,CAAC,QAAoC;IACpE,cAAc,GAAG,QAAQ,CAAC;AAC5B,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,SAAgB,eAAe,CAC7B,MAAgC,EAChC,OAAe;IAEf,MAAM,QAAQ,GACZ,OAAO,MAAM,KAAK,QAAQ;QACxB,CAAC,CAAC,MAAM;QACR,CAAC,CAAC,CAAC,MAAM,EAAE,QAAQ,IAAI,cAAc,EAAE,EAAE,CAAC,CAAC;IAC/C,IAAI,CAAC,QAAQ;QAAE,MAAM,IAAI,4DAAyB,CAAC,OAAO,CAAC,CAAC;IAC5D,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { IEvent } from '../events/event.interface';
|
|
2
|
+
/**
|
|
3
|
+
* The state change a domain method describes: mutable field keys mapped to
|
|
4
|
+
* their new values for `RootEntity.applyPatch()`. Undefined values are ignored.
|
|
5
|
+
* An absent patch leaves fields unchanged.
|
|
6
|
+
*
|
|
7
|
+
* @typeParam TEntity - The aggregate the patch applies to.
|
|
8
|
+
*/
|
|
9
|
+
export type MutationPatch<TEntity> = Partial<TEntity> | void;
|
|
10
|
+
/** A non-empty array of domain events. */
|
|
11
|
+
export type NonEmptyEventArray = readonly [IEvent, ...IEvent[]] | [IEvent, ...IEvent[]];
|
|
12
|
+
export type SingleEvent = IEvent & {
|
|
13
|
+
length?: never;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Configuration for a declarative aggregate mutation and domain event application.
|
|
17
|
+
*
|
|
18
|
+
* @typeParam TEntity - The aggregate the decorated method belongs to.
|
|
19
|
+
*/
|
|
20
|
+
export interface ApplyMutationOptions<TEntity> {
|
|
21
|
+
/**
|
|
22
|
+
* Factory for the domain event(s) recorded once the mutation has completed.
|
|
23
|
+
* It receives the aggregate **after** the patch, `onUpdate()` and the
|
|
24
|
+
* version/timestamp advance, ensuring event payload and metadata snapshot
|
|
25
|
+
* the post-mutation version and `updatedAt`.
|
|
26
|
+
*
|
|
27
|
+
* Must return a single domain event object or a non-empty array of domain events.
|
|
28
|
+
*/
|
|
29
|
+
readonly event: (entity: TEntity) => SingleEvent | NonEmptyEventArray;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Completes a successful domain mutation by advancing `onUpdate()` once and
|
|
33
|
+
* recording events from the resulting state. Preserves the method's return value,
|
|
34
|
+
* including an entity returned by an asynchronous method.
|
|
35
|
+
*
|
|
36
|
+
* Domain methods validate inputs, call `this.applyPatch(...)` for mutable fields,
|
|
37
|
+
* and return `this`. They must not apply events or advance lifecycle state directly.
|
|
38
|
+
* Lifecycle operations are checked before invoking the method.
|
|
39
|
+
*
|
|
40
|
+
* Patch validation and normalization finish before that patch writes any fields.
|
|
41
|
+
* Failures after a successful patch (including later method code, lifecycle hooks,
|
|
42
|
+
* or event construction/application) do not roll back state: discard or reload
|
|
43
|
+
* the aggregate. Rejected methods do not run the completion lifecycle.
|
|
44
|
+
*
|
|
45
|
+
* @typeParam TEntity - The aggregate the decorated method belongs to.
|
|
46
|
+
* @param options - The domain event(s) factory to record for this mutation.
|
|
47
|
+
* @returns A method decorator that performs the full mutation lifecycle.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* export class User extends RootEntity<UserSnapshot> {
|
|
52
|
+
* @Mutable<string>({ normalize: (value) => User.normalizeUsername(value) })
|
|
53
|
+
* private _username: string;
|
|
54
|
+
*
|
|
55
|
+
* @ApplyMutation<User>({ event: (user) => new UserRenamedEvent(user) })
|
|
56
|
+
* rename(name: string): this {
|
|
57
|
+
* this.applyPatch({ username: name });
|
|
58
|
+
* return this;
|
|
59
|
+
* }
|
|
60
|
+
* }
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export declare function ApplyMutation<TEntity = unknown>(options: ApplyMutationOptions<TEntity>): MethodDecorator;
|
|
64
|
+
//# sourceMappingURL=apply-mutation.decorator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apply-mutation.decorator.d.ts","sourceRoot":"","sources":["../../../domain/decorators/apply-mutation.decorator.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,MAAM,2BAA2B,CAAC;AAEnD;;;;;;GAMG;AAEH,MAAM,MAAM,aAAa,CAAC,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;AAE7D,0CAA0C;AAC1C,MAAM,MAAM,kBAAkB,GAC1B,SAAS,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,GAC9B,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;AAE1B,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG;IAAE,MAAM,CAAC,EAAE,KAAK,CAAA;CAAE,CAAC;AAEtD;;;;GAIG;AACH,MAAM,WAAW,oBAAoB,CAAC,OAAO;IAC3C;;;;;;;OAOG;IACH,QAAQ,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,WAAW,GAAG,kBAAkB,CAAC;CACvE;AA4DD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,aAAa,CAAC,OAAO,GAAG,OAAO,EAC7C,OAAO,EAAE,oBAAoB,CAAC,OAAO,CAAC,GACrC,eAAe,CA8BjB"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* Copyright (C) 2026-present Aristotelis — see repository license. */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.ApplyMutation = ApplyMutation;
|
|
5
|
+
function assertLifecycleOperations(entity) {
|
|
6
|
+
for (const method of ['onUpdate', 'apply']) {
|
|
7
|
+
if (typeof entity?.[method] !== 'function') {
|
|
8
|
+
throw new TypeError(`@ApplyMutation requires target instance to implement callable ${method}() method.`);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function completeMutation(entity, result, options) {
|
|
13
|
+
entity.onUpdate();
|
|
14
|
+
const eventOrEvents = options.event(entity);
|
|
15
|
+
if (!Array.isArray(eventOrEvents)) {
|
|
16
|
+
if (eventOrEvents === null || typeof eventOrEvents !== 'object') {
|
|
17
|
+
throw new TypeError(`@ApplyMutation event factory must return a domain event object or non-empty array of events, received: ${String(eventOrEvents)}.`);
|
|
18
|
+
}
|
|
19
|
+
entity.apply(eventOrEvents);
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
if (eventOrEvents.length === 0) {
|
|
23
|
+
throw new TypeError('@ApplyMutation event factory must return at least one domain event.');
|
|
24
|
+
}
|
|
25
|
+
for (let i = 0; i < eventOrEvents.length; i++) {
|
|
26
|
+
const evt = eventOrEvents[i];
|
|
27
|
+
if (evt === null || typeof evt !== 'object') {
|
|
28
|
+
throw new TypeError(`@ApplyMutation event factory returned an invalid event at index ${i}. Expected an event object, received: ${String(evt)}.`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
for (const evt of eventOrEvents) {
|
|
32
|
+
entity.apply(evt);
|
|
33
|
+
}
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Completes a successful domain mutation by advancing `onUpdate()` once and
|
|
38
|
+
* recording events from the resulting state. Preserves the method's return value,
|
|
39
|
+
* including an entity returned by an asynchronous method.
|
|
40
|
+
*
|
|
41
|
+
* Domain methods validate inputs, call `this.applyPatch(...)` for mutable fields,
|
|
42
|
+
* and return `this`. They must not apply events or advance lifecycle state directly.
|
|
43
|
+
* Lifecycle operations are checked before invoking the method.
|
|
44
|
+
*
|
|
45
|
+
* Patch validation and normalization finish before that patch writes any fields.
|
|
46
|
+
* Failures after a successful patch (including later method code, lifecycle hooks,
|
|
47
|
+
* or event construction/application) do not roll back state: discard or reload
|
|
48
|
+
* the aggregate. Rejected methods do not run the completion lifecycle.
|
|
49
|
+
*
|
|
50
|
+
* @typeParam TEntity - The aggregate the decorated method belongs to.
|
|
51
|
+
* @param options - The domain event(s) factory to record for this mutation.
|
|
52
|
+
* @returns A method decorator that performs the full mutation lifecycle.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```ts
|
|
56
|
+
* export class User extends RootEntity<UserSnapshot> {
|
|
57
|
+
* @Mutable<string>({ normalize: (value) => User.normalizeUsername(value) })
|
|
58
|
+
* private _username: string;
|
|
59
|
+
*
|
|
60
|
+
* @ApplyMutation<User>({ event: (user) => new UserRenamedEvent(user) })
|
|
61
|
+
* rename(name: string): this {
|
|
62
|
+
* this.applyPatch({ username: name });
|
|
63
|
+
* return this;
|
|
64
|
+
* }
|
|
65
|
+
* }
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
function ApplyMutation(options) {
|
|
69
|
+
if (!options || typeof options.event !== 'function') {
|
|
70
|
+
throw new TypeError('@ApplyMutation requires an event factory option: @ApplyMutation({ event: (entity) => ... }).');
|
|
71
|
+
}
|
|
72
|
+
return (_target, _propertyKey, descriptor) => {
|
|
73
|
+
const original = descriptor.value;
|
|
74
|
+
descriptor.value = function (...args) {
|
|
75
|
+
assertLifecycleOperations(this);
|
|
76
|
+
const entity = this;
|
|
77
|
+
const result = original.apply(this, args);
|
|
78
|
+
if (result && typeof result.then === 'function') {
|
|
79
|
+
return result.then((value) => completeMutation(entity, value, options));
|
|
80
|
+
}
|
|
81
|
+
return completeMutation(entity, result, options);
|
|
82
|
+
};
|
|
83
|
+
return descriptor;
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=apply-mutation.decorator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apply-mutation.decorator.js","sourceRoot":"","sources":["../../../domain/decorators/apply-mutation.decorator.ts"],"names":[],"mappings":";AAAA,sEAAsE;;AAiItE,sCAgCC;AArHD,SAAS,yBAAyB,CAChC,MAAe;IAEf,KAAK,MAAM,MAAM,IAAI,CAAC,UAAU,EAAE,OAAO,CAAU,EAAE,CAAC;QACpD,IACE,OAAQ,MAAwC,EAAE,CAAC,MAAM,CAAC,KAAK,UAAU,EACzE,CAAC;YACD,MAAM,IAAI,SAAS,CACjB,iEAAiE,MAAM,YAAY,CACpF,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB,CACvB,MAAqB,EACrB,MAAe,EACf,OAAsC;IAEtC,MAAM,CAAC,QAAQ,EAAE,CAAC;IAElB,MAAM,aAAa,GAAG,OAAO,CAAC,KAAK,CAAC,MAAiB,CAAC,CAAC;IAEvD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;QAClC,IAAI,aAAa,KAAK,IAAI,IAAI,OAAO,aAAa,KAAK,QAAQ,EAAE,CAAC;YAChE,MAAM,IAAI,SAAS,CACjB,0GAA0G,MAAM,CAAC,aAAa,CAAC,GAAG,CACnI,CAAC;QACJ,CAAC;QACD,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QAC5B,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,SAAS,CACjB,qEAAqE,CACtE,CAAC;IACJ,CAAC;IACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,aAAa,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC9C,MAAM,GAAG,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;QAC7B,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;YAC5C,MAAM,IAAI,SAAS,CACjB,mEAAmE,CAAC,yCAAyC,MAAM,CAAC,GAAG,CAAC,GAAG,CAC5H,CAAC;QACJ,CAAC;IACH,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,aAAa,EAAE,CAAC;QAChC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACpB,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,SAAgB,aAAa,CAC3B,OAAsC;IAEtC,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;QACpD,MAAM,IAAI,SAAS,CACjB,8FAA8F,CAC/F,CAAC;IACJ,CAAC;IAED,OAAO,CACL,OAAe,EACf,YAA6B,EAC7B,UAA8B,EAC9B,EAAE;QACF,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAe,CAAC;QAE5C,UAAU,CAAC,KAAK,GAAG,UAAU,GAAG,IAAe;YAC7C,yBAAyB,CAAC,IAAI,CAAC,CAAC;YAChC,MAAM,MAAM,GAAG,IAAqB,CAAC;YACrC,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAE1C,IAAI,MAAM,IAAI,OAAQ,MAA2B,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;gBACtE,OAAQ,MAA2B,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CACjD,gBAAgB,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,CACzC,CAAC;YACJ,CAAC;YAED,OAAO,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;QACnD,CAAC,CAAC;QAEF,OAAO,UAAU,CAAC;IACpB,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registration options for a mutable aggregate field.
|
|
3
|
+
*
|
|
4
|
+
* @typeParam TValue - The value type stored in the backing property.
|
|
5
|
+
*/
|
|
6
|
+
export interface MutableOptions<TValue = unknown> {
|
|
7
|
+
/**
|
|
8
|
+
* Public patch key a domain method uses for this field. Defaults to the
|
|
9
|
+
* decorated property name with a single leading underscore removed
|
|
10
|
+
* (`_username` becomes `username`).
|
|
11
|
+
*/
|
|
12
|
+
readonly as?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Invariant check and coercion applied before the value reaches the backing
|
|
15
|
+
* property. Throw a domain exception here to reject the mutation; the
|
|
16
|
+
* aggregate is then left untouched, no version is advanced and no event is
|
|
17
|
+
* recorded.
|
|
18
|
+
*/
|
|
19
|
+
readonly normalize?: (value: TValue) => TValue;
|
|
20
|
+
}
|
|
21
|
+
/** A registered mutable field: its backing property and its normalizer. */
|
|
22
|
+
export interface MutableFieldDefinition {
|
|
23
|
+
readonly propertyKey: string;
|
|
24
|
+
readonly patchKey: string;
|
|
25
|
+
readonly normalize?: (value: unknown) => unknown;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Declares an aggregate property as mutable through domain methods.
|
|
29
|
+
*
|
|
30
|
+
* `RootEntity.applyPatch()` writes only to fields registered here, so the set of properties a
|
|
31
|
+
* mutation may touch is declared once on the field itself instead of being
|
|
32
|
+
* restated in every method that changes it.
|
|
33
|
+
*
|
|
34
|
+
* @param options - Patch key override and the field's normalizer.
|
|
35
|
+
* @returns A property decorator registering the field on the owning class.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* export class User extends RootEntity<UserSnapshot> {
|
|
40
|
+
* @Mutable<string>({ normalize: (value) => User.normalizeUsername(value) })
|
|
41
|
+
* private _username: string;
|
|
42
|
+
* }
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export declare function Mutable<TValue = unknown>(options?: MutableOptions<TValue>): PropertyDecorator;
|
|
46
|
+
/**
|
|
47
|
+
* Resolves every mutable field visible on an instance, including fields
|
|
48
|
+
* declared by base classes.
|
|
49
|
+
*
|
|
50
|
+
* @param instance - The aggregate to inspect.
|
|
51
|
+
* @returns Patch key to field definition, nearest declaration winning.
|
|
52
|
+
*/
|
|
53
|
+
export declare function getMutableFields(instance: object): ReadonlyMap<string, MutableFieldDefinition>;
|
|
54
|
+
//# sourceMappingURL=mutable.decorator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mutable.decorator.d.ts","sourceRoot":"","sources":["../../../domain/decorators/mutable.decorator.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,WAAW,cAAc,CAAC,MAAM,GAAG,OAAO;IAC9C;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IAErB;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;CAChD;AAED,2EAA2E;AAC3E,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;CAClD;AAQD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,OAAO,CAAC,MAAM,GAAG,OAAO,EACtC,OAAO,GAAE,cAAc,CAAC,MAAM,CAAM,GACnC,iBAAiB,CA2BnB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,GACf,WAAW,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAwB7C"}
|