@nestjs/cqrs 10.2.8 → 11.0.0-next.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/.circleci/config.yml +29 -1
  2. package/.husky/commit-msg +0 -3
  3. package/.husky/pre-commit +0 -3
  4. package/dist/classes/command.d.ts +9 -0
  5. package/dist/classes/command.js +7 -0
  6. package/dist/classes/constants.d.ts +1 -0
  7. package/dist/classes/constants.js +4 -0
  8. package/dist/classes/index.d.ts +2 -0
  9. package/dist/classes/index.js +18 -0
  10. package/dist/classes/query.d.ts +9 -0
  11. package/dist/classes/query.js +7 -0
  12. package/dist/command-bus.d.ts +26 -7
  13. package/dist/command-bus.js +57 -21
  14. package/dist/constants.d.ts +1 -1
  15. package/dist/constants.js +1 -0
  16. package/dist/cqrs.module.d.ts +5 -4
  17. package/dist/cqrs.module.js +21 -11
  18. package/dist/decorators/command-handler.decorator.d.ts +3 -1
  19. package/dist/decorators/command-handler.decorator.js +8 -3
  20. package/dist/decorators/events-handler.decorator.d.ts +3 -1
  21. package/dist/decorators/events-handler.decorator.js +9 -2
  22. package/dist/decorators/index.d.ts +1 -0
  23. package/dist/decorators/index.js +6 -1
  24. package/dist/decorators/publishable.decorator.d.ts +5 -0
  25. package/dist/decorators/publishable.decorator.js +13 -0
  26. package/dist/decorators/query-handler.decorator.d.ts +3 -1
  27. package/dist/decorators/query-handler.decorator.js +8 -3
  28. package/dist/event-bus.d.ts +51 -10
  29. package/dist/event-bus.js +108 -31
  30. package/dist/event-publisher.d.ts +5 -2
  31. package/dist/event-publisher.js +10 -8
  32. package/dist/exceptions/command-not-found.exception.js +1 -1
  33. package/dist/exceptions/index.d.ts +1 -0
  34. package/dist/exceptions/index.js +6 -1
  35. package/dist/exceptions/invalid-command-handler.exception.js +1 -1
  36. package/dist/exceptions/invalid-events-handler.exception.js +1 -1
  37. package/dist/exceptions/invalid-query-handler.exception.js +1 -1
  38. package/dist/exceptions/invalid-saga.exception.js +1 -1
  39. package/dist/exceptions/query-not-found.exception.js +1 -1
  40. package/dist/exceptions/unsupported-saga-scope.exception.d.ts +3 -0
  41. package/dist/exceptions/unsupported-saga-scope.exception.js +9 -0
  42. package/dist/helpers/default-event-id-provider.d.ts +7 -0
  43. package/dist/helpers/default-event-id-provider.js +10 -0
  44. package/dist/index.d.ts +2 -0
  45. package/dist/index.js +7 -1
  46. package/dist/interfaces/commands/command-bus.interface.d.ts +18 -1
  47. package/dist/interfaces/commands/command-handler.interface.d.ts +9 -2
  48. package/dist/interfaces/cqrs-module-options.interface.d.ts +38 -8
  49. package/dist/interfaces/events/event-bus.interface.d.ts +41 -4
  50. package/dist/interfaces/events/event-id-provider.interface.d.ts +12 -0
  51. package/dist/interfaces/events/event-id-provider.interface.js +2 -0
  52. package/dist/interfaces/events/event-publisher.interface.d.ts +7 -4
  53. package/dist/interfaces/index.d.ts +2 -0
  54. package/dist/interfaces/index.js +7 -1
  55. package/dist/interfaces/providers-introspection-result.interface.d.ts +10 -0
  56. package/dist/interfaces/providers-introspection-result.interface.js +2 -0
  57. package/dist/interfaces/queries/query-bus.interface.d.ts +18 -1
  58. package/dist/interfaces/queries/query-handler.interface.d.ts +9 -2
  59. package/dist/interfaces/saga.type.d.ts +1 -1
  60. package/dist/operators/index.js +5 -1
  61. package/dist/operators/of-type.d.ts +1 -1
  62. package/dist/operators/of-type.js +1 -2
  63. package/dist/query-bus.d.ts +26 -6
  64. package/dist/query-bus.js +49 -20
  65. package/dist/scopes/async.context.d.ts +30 -0
  66. package/dist/scopes/async.context.js +58 -0
  67. package/dist/scopes/index.d.ts +1 -0
  68. package/dist/scopes/index.js +17 -0
  69. package/dist/services/explorer.service.d.ts +4 -6
  70. package/dist/services/explorer.service.js +14 -14
  71. package/dist/storages/aggregate-root.storage.d.ts +8 -0
  72. package/dist/storages/aggregate-root.storage.js +21 -0
  73. package/dist/unhandled-exception-bus.d.ts +4 -3
  74. package/dist/unhandled-exception-bus.js +18 -5
  75. package/dist/utils/index.js +5 -1
  76. package/jest.config.js +5 -0
  77. package/package.json +29 -17
  78. package/renovate.json +8 -7
  79. package/test/e2e/basic-flows.spec.ts +106 -0
  80. package/test/e2e/generics.spec.ts +198 -0
  81. package/test/e2e/request-scope.spec.ts +141 -0
  82. package/test/e2e/unhandled-exceptions.spec.ts +73 -0
  83. package/test/jest-e2e.json +9 -0
  84. package/test/utils/wait-immediate.ts +2 -0
  85. package/tsconfig.build.json +10 -0
@@ -1,20 +1,23 @@
1
1
  import { OnModuleDestroy, Type } from '@nestjs/common';
2
2
  import { ModuleRef } from '@nestjs/core';
3
+ import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper';
3
4
  import { Observable, Subscription } from 'rxjs';
4
5
  import { CommandBus } from './command-bus';
5
- import { IEvent, IEventBus, IEventHandler, IEventPublisher, ISaga } from './interfaces';
6
+ import { CqrsModuleOptions, EventIdProvider, IEvent, IEventBus, IEventHandler, IEventPublisher, ISaga } from './interfaces';
7
+ import { AsyncContext } from './scopes';
6
8
  import { UnhandledExceptionBus } from './unhandled-exception-bus';
7
9
  import { ObservableBus } from './utils';
8
- export declare type EventHandlerType<EventBase extends IEvent = IEvent> = Type<IEventHandler<EventBase>>;
10
+ export type EventHandlerType<EventBase extends IEvent = IEvent> = Type<IEventHandler<EventBase>>;
9
11
  export declare class EventBus<EventBase extends IEvent = IEvent> extends ObservableBus<EventBase> implements IEventBus<EventBase>, OnModuleDestroy {
10
12
  private readonly commandBus;
11
13
  private readonly moduleRef;
12
14
  private readonly unhandledExceptionBus;
13
- protected getEventId: (event: EventBase) => string | null;
15
+ private readonly options?;
16
+ protected eventIdProvider: EventIdProvider<EventBase>;
14
17
  protected readonly subscriptions: Subscription[];
15
18
  private _publisher;
16
19
  private readonly _logger;
17
- constructor(commandBus: CommandBus, moduleRef: ModuleRef, unhandledExceptionBus: UnhandledExceptionBus);
20
+ constructor(commandBus: CommandBus, moduleRef: ModuleRef, unhandledExceptionBus: UnhandledExceptionBus, options?: CqrsModuleOptions | undefined);
18
21
  /**
19
22
  * Returns the publisher.
20
23
  * Default publisher is `DefaultPubSub` (in memory).
@@ -31,16 +34,54 @@ export declare class EventBus<EventBase extends IEvent = IEvent> extends Observa
31
34
  * Publishes an event.
32
35
  * @param event The event to publish.
33
36
  */
34
- publish<TEvent extends EventBase, TContext = unknown>(event: TEvent, context?: TContext): any;
37
+ publish<TEvent extends EventBase>(event: TEvent): any;
38
+ /**
39
+ * Publishes an event.
40
+ * @param event The event to publish.
41
+ * @param asyncContext Async context
42
+ */
43
+ publish<TEvent extends EventBase>(event: TEvent, asyncContext: AsyncContext): any;
44
+ /**
45
+ * Publishes an event.
46
+ * @param event The event to publish.
47
+ * @param dispatcherContext Dispatcher context
48
+ */
49
+ publish<TEvent extends EventBase, TContext = unknown>(event: TEvent, dispatcherContext: TContext): any;
50
+ /**
51
+ * Publishes an event.
52
+ * @param event The event to publish.
53
+ * @param dispatcherContext Dispatcher context
54
+ * @param asyncContext Async context
55
+ */
56
+ publish<TEvent extends EventBase, TContext = unknown>(event: TEvent, dispatcherContext: TContext, asyncContext: AsyncContext): any;
57
+ /**
58
+ * Publishes multiple events.
59
+ * @param events The events to publish.
60
+ */
61
+ publishAll<TEvent extends EventBase>(events: TEvent[]): any;
62
+ /**
63
+ * Publishes multiple events.
64
+ * @param events The events to publish.
65
+ * @param asyncContext Async context
66
+ */
67
+ publishAll<TEvent extends EventBase>(events: TEvent[], asyncContext: AsyncContext): any;
68
+ /**
69
+ * Publishes multiple events.
70
+ * @param events The events to publish.
71
+ * @param dispatcherContext Dispatcher context
72
+ */
73
+ publishAll<TEvent extends EventBase, TContext = unknown>(events: TEvent[], dispatcherContext: TContext): any;
35
74
  /**
36
75
  * Publishes multiple events.
37
76
  * @param events The events to publish.
77
+ * @param dispatcherContext Dispatcher context
78
+ * @param asyncContext Async context
38
79
  */
39
- publishAll<TEvent extends EventBase, TContext = unknown>(events: TEvent[], context?: TContext): any;
40
- bind(handler: IEventHandler<EventBase>, id: string): void;
41
- registerSagas(types?: Type<unknown>[]): void;
42
- register(handlers?: EventHandlerType<EventBase>[]): void;
43
- protected registerHandler(handler: EventHandlerType<EventBase>): void;
80
+ publishAll<TEvent extends EventBase, TContext = unknown>(events: TEvent[], dispatcherContext: TContext, asyncContext: AsyncContext): any;
81
+ bind(handler: InstanceWrapper<IEventHandler<EventBase>>, id: string): void;
82
+ registerSagas(wrappers?: InstanceWrapper<object>[]): void;
83
+ register(handlers?: InstanceWrapper<IEventHandler<EventBase>>[]): void;
84
+ protected registerHandler(handler: InstanceWrapper<IEventHandler<EventBase>>): void;
44
85
  protected ofEventId(id: string): Observable<EventBase>;
45
86
  protected registerSaga(saga: ISaga<EventBase>): void;
46
87
  private reflectEvents;
package/dist/event-bus.js CHANGED
@@ -8,6 +8,9 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
8
8
  var __metadata = (this && this.__metadata) || function (k, v) {
9
9
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
10
  };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
11
14
  var EventBus_1;
12
15
  Object.defineProperty(exports, "__esModule", { value: true });
13
16
  exports.EventBus = void 0;
@@ -16,22 +19,31 @@ const core_1 = require("@nestjs/core");
16
19
  const rxjs_1 = require("rxjs");
17
20
  const operators_1 = require("rxjs/operators");
18
21
  const command_bus_1 = require("./command-bus");
19
- const constants_1 = require("./decorators/constants");
22
+ const constants_1 = require("./constants");
23
+ const constants_2 = require("./decorators/constants");
20
24
  const exceptions_1 = require("./exceptions");
21
- const default_get_event_id_1 = require("./helpers/default-get-event-id");
25
+ const default_event_id_provider_1 = require("./helpers/default-event-id-provider");
22
26
  const default_pubsub_1 = require("./helpers/default-pubsub");
27
+ const scopes_1 = require("./scopes");
23
28
  const unhandled_exception_bus_1 = require("./unhandled-exception-bus");
24
29
  const utils_1 = require("./utils");
25
30
  let EventBus = EventBus_1 = class EventBus extends utils_1.ObservableBus {
26
- constructor(commandBus, moduleRef, unhandledExceptionBus) {
31
+ constructor(commandBus, moduleRef, unhandledExceptionBus, options) {
27
32
  super();
28
33
  this.commandBus = commandBus;
29
34
  this.moduleRef = moduleRef;
30
35
  this.unhandledExceptionBus = unhandledExceptionBus;
36
+ this.options = options;
31
37
  this._logger = new common_1.Logger(EventBus_1.name);
32
38
  this.subscriptions = [];
33
- this.getEventId = default_get_event_id_1.defaultGetEventId;
34
- this.useDefaultPublisher();
39
+ this.eventIdProvider =
40
+ this.options?.eventIdProvider ?? default_event_id_provider_1.defaultEventIdProvider;
41
+ if (this.options?.eventPublisher) {
42
+ this._publisher = this.options.eventPublisher;
43
+ }
44
+ else {
45
+ this.useDefaultPublisher();
46
+ }
35
47
  }
36
48
  /**
37
49
  * Returns the publisher.
@@ -54,24 +66,61 @@ let EventBus = EventBus_1 = class EventBus extends utils_1.ObservableBus {
54
66
  /**
55
67
  * Publishes an event.
56
68
  * @param event The event to publish.
69
+ * @param dispatcherOrAsyncContext Dispatcher context or async context
70
+ * @param asyncContext Async context
57
71
  */
58
- publish(event, context) {
59
- return this._publisher.publish(event, context);
72
+ publish(event, dispatcherOrAsyncContext, asyncContext) {
73
+ if (!asyncContext && dispatcherOrAsyncContext instanceof scopes_1.AsyncContext) {
74
+ asyncContext = dispatcherOrAsyncContext;
75
+ dispatcherOrAsyncContext = undefined;
76
+ }
77
+ if (asyncContext) {
78
+ asyncContext.attachTo(event);
79
+ }
80
+ return this._publisher.publish(event, dispatcherOrAsyncContext, asyncContext);
60
81
  }
61
82
  /**
62
83
  * Publishes multiple events.
63
84
  * @param events The events to publish.
85
+ * @param dispatcherOrAsyncContext Dispatcher context or async context
86
+ * @param asyncContext Async context
64
87
  */
65
- publishAll(events, context) {
88
+ publishAll(events, dispatcherOrAsyncContext, asyncContext) {
89
+ if (!asyncContext && dispatcherOrAsyncContext instanceof scopes_1.AsyncContext) {
90
+ asyncContext = dispatcherOrAsyncContext;
91
+ dispatcherOrAsyncContext = undefined;
92
+ }
93
+ if (asyncContext) {
94
+ events.forEach((event) => {
95
+ if (scopes_1.AsyncContext.isAttached(event)) {
96
+ return;
97
+ }
98
+ asyncContext.attachTo(event);
99
+ });
100
+ }
66
101
  if (this._publisher.publishAll) {
67
- return this._publisher.publishAll(events, context);
102
+ return this._publisher.publishAll(events, dispatcherOrAsyncContext, asyncContext);
68
103
  }
69
- return (events || []).map((event) => this._publisher.publish(event, context));
104
+ return (events || []).map((event) => this._publisher.publish(event, dispatcherOrAsyncContext, asyncContext));
70
105
  }
71
106
  bind(handler, id) {
72
107
  const stream$ = id ? this.ofEventId(id) : this.subject$;
108
+ const deferred = handler.isDependencyTreeStatic()
109
+ ? (event) => () => {
110
+ return Promise.resolve(handler.instance.handle(event));
111
+ }
112
+ : (event) => async () => {
113
+ const asyncContext = scopes_1.AsyncContext.of(event) ?? new scopes_1.AsyncContext();
114
+ const instance = await this.moduleRef.resolve(handler.metatype, asyncContext.id, {
115
+ strict: false,
116
+ });
117
+ return instance.handle(event);
118
+ };
73
119
  const subscription = stream$
74
- .pipe((0, operators_1.mergeMap)((event) => (0, rxjs_1.defer)(() => Promise.resolve(handler.handle(event))).pipe((0, operators_1.catchError)((error) => {
120
+ .pipe((0, operators_1.mergeMap)((event) => (0, rxjs_1.defer)(deferred(event)).pipe((0, operators_1.catchError)((error) => {
121
+ if (this.options?.rethrowUnhandled) {
122
+ throw error;
123
+ }
75
124
  const unhandledError = this.mapToUnhandledErrorInfo(event, error);
76
125
  this.unhandledExceptionBus.publish(unhandledError);
77
126
  this._logger.error(`"${handler.constructor.name}" has thrown an unhandled exception.`, error);
@@ -80,15 +129,24 @@ let EventBus = EventBus_1 = class EventBus extends utils_1.ObservableBus {
80
129
  .subscribe();
81
130
  this.subscriptions.push(subscription);
82
131
  }
83
- registerSagas(types = []) {
84
- const sagas = types
85
- .map((target) => {
86
- const metadata = Reflect.getMetadata(constants_1.SAGA_METADATA, target) || [];
87
- const instance = this.moduleRef.get(target, { strict: false });
88
- if (!instance) {
89
- throw new exceptions_1.InvalidSagaException();
132
+ registerSagas(wrappers = []) {
133
+ const sagas = wrappers
134
+ .map((wrapper) => {
135
+ const { metatype: target } = wrapper;
136
+ const metadata = Reflect.getMetadata(constants_2.SAGA_METADATA, target) ?? [];
137
+ if (!wrapper.isDependencyTreeStatic()) {
138
+ throw new exceptions_1.UnsupportedSagaScopeException();
90
139
  }
91
- return metadata.map((key) => instance[key].bind(instance));
140
+ const instance = wrapper.instance;
141
+ return metadata.map((key) => {
142
+ const sagaFn = instance[key].bind(instance);
143
+ Object.defineProperty(sagaFn, 'name', {
144
+ value: key,
145
+ writable: false,
146
+ configurable: false,
147
+ });
148
+ return sagaFn;
149
+ });
92
150
  })
93
151
  .reduce((a, b) => a.concat(b), []);
94
152
  sagas.forEach((saga) => this.registerSaga(saga));
@@ -97,15 +155,21 @@ let EventBus = EventBus_1 = class EventBus extends utils_1.ObservableBus {
97
155
  handlers.forEach((handler) => this.registerHandler(handler));
98
156
  }
99
157
  registerHandler(handler) {
100
- const instance = this.moduleRef.get(handler, { strict: false });
101
- if (!instance) {
102
- return;
103
- }
104
- const events = this.reflectEvents(handler);
105
- events.map((event) => this.bind(instance, (0, default_get_event_id_1.defaultReflectEventId)(event)));
158
+ const typeRef = handler.metatype;
159
+ const events = this.reflectEvents(typeRef);
160
+ events.forEach((event) => {
161
+ const eventId = this.eventIdProvider.getEventId(event);
162
+ this.bind(handler, eventId);
163
+ });
106
164
  }
107
165
  ofEventId(id) {
108
- return this.subject$.pipe((0, operators_1.filter)((event) => this.getEventId(event) === id));
166
+ return this.subject$.pipe((0, operators_1.filter)((event) => {
167
+ const { constructor } = Object.getPrototypeOf(event);
168
+ if (!constructor) {
169
+ return false;
170
+ }
171
+ return this.eventIdProvider.getEventId(constructor) === id;
172
+ }));
109
173
  }
110
174
  registerSaga(saga) {
111
175
  if (typeof saga !== 'function') {
@@ -116,7 +180,18 @@ let EventBus = EventBus_1 = class EventBus extends utils_1.ObservableBus {
116
180
  throw new exceptions_1.InvalidSagaException();
117
181
  }
118
182
  const subscription = stream$
119
- .pipe((0, operators_1.filter)((e) => !!e), (0, operators_1.mergeMap)((command) => (0, rxjs_1.defer)(() => this.commandBus.execute(command)).pipe((0, operators_1.catchError)((error) => {
183
+ .pipe((0, operators_1.filter)((e) => !!e), (0, operators_1.catchError)((error) => {
184
+ if (this.options?.rethrowUnhandled) {
185
+ throw error;
186
+ }
187
+ const unhandledError = this.mapToUnhandledErrorInfo(saga.name, error);
188
+ this.unhandledExceptionBus.publish(unhandledError);
189
+ this._logger.error(`Saga "${saga.name}" has thrown an unhandled exception.`, error);
190
+ return (0, rxjs_1.of)();
191
+ }), (0, operators_1.mergeMap)((command) => (0, rxjs_1.defer)(() => this.commandBus.execute(command)).pipe((0, operators_1.catchError)((error) => {
192
+ if (this.options?.rethrowUnhandled) {
193
+ throw error;
194
+ }
120
195
  const unhandledError = this.mapToUnhandledErrorInfo(command, error);
121
196
  this.unhandledExceptionBus.publish(unhandledError);
122
197
  this._logger.error(`Command handler which execution was triggered by Saga has thrown an unhandled exception.`, error);
@@ -126,7 +201,7 @@ let EventBus = EventBus_1 = class EventBus extends utils_1.ObservableBus {
126
201
  this.subscriptions.push(subscription);
127
202
  }
128
203
  reflectEvents(handler) {
129
- return Reflect.getMetadata(constants_1.EVENTS_HANDLER_METADATA, handler);
204
+ return Reflect.getMetadata(constants_2.EVENTS_HANDLER_METADATA, handler);
130
205
  }
131
206
  useDefaultPublisher() {
132
207
  this._publisher = new default_pubsub_1.DefaultPubSub(this.subject$);
@@ -138,10 +213,12 @@ let EventBus = EventBus_1 = class EventBus extends utils_1.ObservableBus {
138
213
  };
139
214
  }
140
215
  };
141
- EventBus = EventBus_1 = __decorate([
216
+ exports.EventBus = EventBus;
217
+ exports.EventBus = EventBus = EventBus_1 = __decorate([
142
218
  (0, common_1.Injectable)(),
219
+ __param(3, (0, common_1.Optional)()),
220
+ __param(3, (0, common_1.Inject)(constants_1.CQRS_MODULE_OPTIONS)),
143
221
  __metadata("design:paramtypes", [command_bus_1.CommandBus,
144
222
  core_1.ModuleRef,
145
- unhandled_exception_bus_1.UnhandledExceptionBus])
223
+ unhandled_exception_bus_1.UnhandledExceptionBus, Object])
146
224
  ], EventBus);
147
- exports.EventBus = EventBus;
@@ -1,6 +1,7 @@
1
1
  import { AggregateRoot } from './aggregate-root';
2
2
  import { EventBus } from './event-bus';
3
3
  import { IEvent } from './interfaces';
4
+ import { AsyncContext } from './scopes';
4
5
  export interface Constructor<T> {
5
6
  new (...args: any[]): T;
6
7
  }
@@ -11,12 +12,14 @@ export declare class EventPublisher<EventBase extends IEvent = IEvent> {
11
12
  * Merge the event publisher into the provided class.
12
13
  * This is required to make `publish` and `publishAll` available on the `AgreggateRoot` class.
13
14
  * @param metatype The class to merge into.
15
+ * @param asyncContext The async context (if scoped).
14
16
  */
15
- mergeClassContext<T extends Constructor<AggregateRoot<EventBase>>>(metatype: T): T;
17
+ mergeClassContext<T extends Constructor<AggregateRoot<EventBase>>>(metatype: T, asyncContext?: AsyncContext): T;
16
18
  /**
17
19
  * Merge the event publisher into the provided object.
18
20
  * This is required to make `publish` and `publishAll` available on the `AgreggateRoot` class instance.
19
21
  * @param object The object to merge into.
22
+ * @param asyncContext The async context (if scoped).
20
23
  */
21
- mergeObjectContext<T extends AggregateRoot<EventBase>>(object: T): T;
24
+ mergeObjectContext<T extends AggregateRoot<EventBase>>(object: T, asyncContext?: AsyncContext): T;
22
25
  }
@@ -20,15 +20,16 @@ let EventPublisher = class EventPublisher {
20
20
  * Merge the event publisher into the provided class.
21
21
  * This is required to make `publish` and `publishAll` available on the `AgreggateRoot` class.
22
22
  * @param metatype The class to merge into.
23
+ * @param asyncContext The async context (if scoped).
23
24
  */
24
- mergeClassContext(metatype) {
25
+ mergeClassContext(metatype, asyncContext) {
25
26
  const eventBus = this.eventBus;
26
27
  return class extends metatype {
27
28
  publish(event) {
28
- eventBus.publish(event, this);
29
+ eventBus.publish(event, this, asyncContext);
29
30
  }
30
31
  publishAll(events) {
31
- eventBus.publishAll(events, this);
32
+ eventBus.publishAll(events, this, asyncContext);
32
33
  }
33
34
  };
34
35
  }
@@ -36,20 +37,21 @@ let EventPublisher = class EventPublisher {
36
37
  * Merge the event publisher into the provided object.
37
38
  * This is required to make `publish` and `publishAll` available on the `AgreggateRoot` class instance.
38
39
  * @param object The object to merge into.
40
+ * @param asyncContext The async context (if scoped).
39
41
  */
40
- mergeObjectContext(object) {
42
+ mergeObjectContext(object, asyncContext) {
41
43
  const eventBus = this.eventBus;
42
44
  object.publish = (event) => {
43
- eventBus.publish(event, object);
45
+ eventBus.publish(event, object, asyncContext);
44
46
  };
45
47
  object.publishAll = (events) => {
46
- eventBus.publishAll(events, object);
48
+ eventBus.publishAll(events, object, asyncContext);
47
49
  };
48
50
  return object;
49
51
  }
50
52
  };
51
- EventPublisher = __decorate([
53
+ exports.EventPublisher = EventPublisher;
54
+ exports.EventPublisher = EventPublisher = __decorate([
52
55
  (0, common_1.Injectable)(),
53
56
  __metadata("design:paramtypes", [event_bus_1.EventBus])
54
57
  ], EventPublisher);
55
- exports.EventPublisher = EventPublisher;
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CommandHandlerNotFoundException = void 0;
4
4
  class CommandHandlerNotFoundException extends Error {
5
5
  constructor(commandName) {
6
- super(`The command handler for the "${commandName}" command was not found!`);
6
+ super(`No handler found for the command: "${commandName}".`);
7
7
  }
8
8
  }
9
9
  exports.CommandHandlerNotFoundException = CommandHandlerNotFoundException;
@@ -4,3 +4,4 @@ export * from './invalid-events-handler.exception';
4
4
  export * from './invalid-query-handler.exception';
5
5
  export * from './invalid-saga.exception';
6
6
  export * from './query-not-found.exception';
7
+ export * from './unsupported-saga-scope.exception';
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
5
9
  }) : (function(o, m, k, k2) {
6
10
  if (k2 === undefined) k2 = k;
7
11
  o[k2] = m[k];
@@ -16,3 +20,4 @@ __exportStar(require("./invalid-events-handler.exception"), exports);
16
20
  __exportStar(require("./invalid-query-handler.exception"), exports);
17
21
  __exportStar(require("./invalid-saga.exception"), exports);
18
22
  __exportStar(require("./query-not-found.exception"), exports);
23
+ __exportStar(require("./unsupported-saga-scope.exception"), exports);
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.InvalidCommandHandlerException = void 0;
4
4
  class InvalidCommandHandlerException extends Error {
5
5
  constructor() {
6
- super(`Invalid command handler exception (missing @CommandHandler() decorator?)`);
6
+ super(`An invalid command handler has been provided. Please ensure that the provided handler is a class annotated with @CommandHandler and contains an 'execute' method.`);
7
7
  }
8
8
  }
9
9
  exports.InvalidCommandHandlerException = InvalidCommandHandlerException;
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.InvalidEventsHandlerException = void 0;
4
4
  class InvalidEventsHandlerException extends Error {
5
5
  constructor() {
6
- super(`Invalid event handler exception (missing @EventsHandler() decorator?)`);
6
+ super(`An invalid events handler has been provided. Please ensure that the provided handler is a class annotated with @EventsHandler and contains a 'handle' method.`);
7
7
  }
8
8
  }
9
9
  exports.InvalidEventsHandlerException = InvalidEventsHandlerException;
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.InvalidQueryHandlerException = void 0;
4
4
  class InvalidQueryHandlerException extends Error {
5
5
  constructor() {
6
- super(`Invalid query handler exception (missing @QueryHandler() decorator?)`);
6
+ super(`An invalid query handler has been provided. Please ensure that the provided handler is a class annotated with @QueryHandler and contains an 'execute' method.`);
7
7
  }
8
8
  }
9
9
  exports.InvalidQueryHandlerException = InvalidQueryHandlerException;
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.InvalidSagaException = void 0;
4
4
  class InvalidSagaException extends Error {
5
5
  constructor() {
6
- super(`Invalid saga exception. Each saga should return an Observable object`);
6
+ super(`Invalid saga. Each saga should return Observable stream.`);
7
7
  }
8
8
  }
9
9
  exports.InvalidSagaException = InvalidSagaException;
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.QueryHandlerNotFoundException = void 0;
4
4
  class QueryHandlerNotFoundException extends Error {
5
5
  constructor(queryName) {
6
- super(`The query handler for the "${queryName}" query was not found!`);
6
+ super(`No handler found for the query: "${queryName}"`);
7
7
  }
8
8
  }
9
9
  exports.QueryHandlerNotFoundException = QueryHandlerNotFoundException;
@@ -0,0 +1,3 @@
1
+ export declare class UnsupportedSagaScopeException extends Error {
2
+ constructor();
3
+ }
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UnsupportedSagaScopeException = void 0;
4
+ class UnsupportedSagaScopeException extends Error {
5
+ constructor() {
6
+ super(`Request-scoped saga hosts are not supported. Instead, you should retrieve async-context-specific instances within the saga itself, using the "ModuleRef" provider.`);
7
+ }
8
+ }
9
+ exports.UnsupportedSagaScopeException = UnsupportedSagaScopeException;
@@ -0,0 +1,7 @@
1
+ import { Type } from '@nestjs/common';
2
+ import { EventIdProvider, IEvent } from '../interfaces';
3
+ declare class DefaultEventIdProvider<EventBase extends IEvent = IEvent> implements EventIdProvider<EventBase> {
4
+ getEventId(event: Type<EventBase>): string | null;
5
+ }
6
+ export declare const defaultEventIdProvider: DefaultEventIdProvider<IEvent>;
7
+ export {};
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defaultEventIdProvider = void 0;
4
+ const constants_1 = require("../decorators/constants");
5
+ class DefaultEventIdProvider {
6
+ getEventId(event) {
7
+ return Reflect.getMetadata(constants_1.EVENT_METADATA, event)?.id ?? null;
8
+ }
9
+ }
10
+ exports.defaultEventIdProvider = new DefaultEventIdProvider();
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './aggregate-root';
2
+ export * from './classes';
2
3
  export * from './command-bus';
3
4
  export * from './cqrs.module';
4
5
  export * from './decorators';
@@ -8,5 +9,6 @@ export * from './exceptions';
8
9
  export * from './interfaces';
9
10
  export * from './operators';
10
11
  export * from './query-bus';
12
+ export * from './scopes';
11
13
  export * from './unhandled-exception-bus';
12
14
  export * from './utils';
package/dist/index.js CHANGED
@@ -7,7 +7,11 @@
7
7
  */
8
8
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
9
  if (k2 === undefined) k2 = k;
10
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
11
15
  }) : (function(o, m, k, k2) {
12
16
  if (k2 === undefined) k2 = k;
13
17
  o[k2] = m[k];
@@ -17,6 +21,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
17
21
  };
18
22
  Object.defineProperty(exports, "__esModule", { value: true });
19
23
  __exportStar(require("./aggregate-root"), exports);
24
+ __exportStar(require("./classes"), exports);
20
25
  __exportStar(require("./command-bus"), exports);
21
26
  __exportStar(require("./cqrs.module"), exports);
22
27
  __exportStar(require("./decorators"), exports);
@@ -26,5 +31,6 @@ __exportStar(require("./exceptions"), exports);
26
31
  __exportStar(require("./interfaces"), exports);
27
32
  __exportStar(require("./operators"), exports);
28
33
  __exportStar(require("./query-bus"), exports);
34
+ __exportStar(require("./scopes"), exports);
29
35
  __exportStar(require("./unhandled-exception-bus"), exports);
30
36
  __exportStar(require("./utils"), exports);
@@ -1,3 +1,5 @@
1
+ import { Command } from '../../classes';
2
+ import { AsyncContext } from '../../scopes/async.context';
1
3
  import { ICommand } from './command.interface';
2
4
  /**
3
5
  * Represents a command bus.
@@ -6,6 +8,21 @@ export interface ICommandBus<CommandBase extends ICommand = ICommand> {
6
8
  /**
7
9
  * Executes a command.
8
10
  * @param command The command to execute.
11
+ * @returns A promise that, when resolved, will contain the result returned by the command's handler.
9
12
  */
10
- execute<T extends CommandBase, R = any>(command: T): Promise<R>;
13
+ execute<R = void>(command: Command<R>): Promise<R>;
14
+ /**
15
+ * Executes a command.
16
+ * @param command The command to execute.
17
+ * @param context The context to use. Optional.
18
+ * @returns A promise that, when resolved, will contain the result returned by the command's handler.
19
+ */
20
+ execute<R = void>(command: Command<R>, context?: AsyncContext): Promise<R>;
21
+ /**
22
+ * Executes a command.
23
+ * @param command The command to execute.
24
+ * @param context The context to use. Optional.
25
+ * @returns A promise that, when resolved, will contain the result returned by the command's handler.
26
+ */
27
+ execute<T extends CommandBase, R = any>(command: T, context?: AsyncContext): Promise<R>;
11
28
  }
@@ -1,12 +1,19 @@
1
+ import { Command } from '../../classes';
1
2
  import { ICommand } from './command.interface';
2
3
  /**
3
4
  * Represents a command handler.
4
5
  * Command handlers are used to execute commands.
5
6
  */
6
- export interface ICommandHandler<TCommand extends ICommand = any, TResult = any> {
7
+ export type ICommandHandler<TCommand extends ICommand = any, TResult = any> = TCommand extends Command<infer InferredCommandResult> ? {
8
+ /**
9
+ * Executes a command.
10
+ * @param command The command to execute.
11
+ */
12
+ execute(command: TCommand): Promise<InferredCommandResult>;
13
+ } : {
7
14
  /**
8
15
  * Executes a command.
9
16
  * @param command The command to execute.
10
17
  */
11
18
  execute(command: TCommand): Promise<TResult>;
12
- }
19
+ };
@@ -1,10 +1,40 @@
1
- import { Type } from '@nestjs/common';
2
- import { ICommandHandler } from './commands/command-handler.interface';
3
- import { IEventHandler } from './events/event-handler.interface';
4
- import { IQueryHandler } from './queries/query-handler.interface';
1
+ import { ICommandPublisher } from './commands/command-publisher.interface';
2
+ import { EventIdProvider } from './events/event-id-provider.interface';
3
+ import { IEventPublisher } from './events/event-publisher.interface';
4
+ import { IUnhandledExceptionPublisher } from './exceptions/unhandled-exception-publisher.interface';
5
+ import { IQueryPublisher } from './queries/query-publisher.interface';
6
+ /**
7
+ * Options for the CqrsModule.
8
+ */
5
9
  export interface CqrsModuleOptions {
6
- events?: Type<IEventHandler>[];
7
- queries?: Type<IQueryHandler>[];
8
- commands?: Type<ICommandHandler>[];
9
- sagas?: Type<any>[];
10
+ /**
11
+ * Command publisher to use for publishing commands.
12
+ * @default DefaultCommandPubSub
13
+ */
14
+ commandPublisher?: ICommandPublisher;
15
+ /**
16
+ * Event publisher to use for publishing events.
17
+ * @default DefaultPubSub
18
+ */
19
+ eventPublisher?: IEventPublisher;
20
+ /**
21
+ * Query publisher to use for publishing queries.
22
+ * @default DefaultQueryPubSub
23
+ */
24
+ queryPublisher?: IQueryPublisher;
25
+ /**
26
+ * Unhandled exception publisher to use for publishing unhandled exceptions.
27
+ * @default DefaultUnhandledExceptionPubSub
28
+ */
29
+ unhandledExceptionPublisher?: IUnhandledExceptionPublisher;
30
+ /**
31
+ * Event ID provider to use for retrieving event IDs by event instances.
32
+ * @default DefaultEventIdProvider
33
+ */
34
+ eventIdProvider?: EventIdProvider;
35
+ /**
36
+ * Whether to rethrow unhandled exceptions.
37
+ * @default false
38
+ */
39
+ rethrowUnhandled?: boolean;
10
40
  }