@nestjs/cqrs 9.0.4 → 10.0.1

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 (33) hide show
  1. package/.circleci/config.yml +8 -9
  2. package/dist/aggregate-root.d.ts +47 -2
  3. package/dist/aggregate-root.js +46 -1
  4. package/dist/command-bus.d.ts +14 -0
  5. package/dist/command-bus.js +14 -0
  6. package/dist/cqrs.module.d.ts +3 -3
  7. package/dist/cqrs.module.js +22 -8
  8. package/dist/event-bus.d.ts +22 -1
  9. package/dist/event-bus.js +47 -10
  10. package/dist/event-publisher.d.ts +11 -1
  11. package/dist/event-publisher.js +10 -0
  12. package/dist/helpers/default-get-event-id.js +1 -2
  13. package/dist/helpers/default-unhandled-exception-pubsub.d.ts +10 -0
  14. package/dist/helpers/default-unhandled-exception-pubsub.js +15 -0
  15. package/dist/index.d.ts +1 -0
  16. package/dist/index.js +1 -0
  17. package/dist/interfaces/commands/command-bus.interface.d.ts +7 -0
  18. package/dist/interfaces/commands/command-handler.interface.d.ts +8 -0
  19. package/dist/interfaces/events/event-bus.interface.d.ts +11 -0
  20. package/dist/interfaces/events/event-handler.interface.d.ts +7 -0
  21. package/dist/interfaces/exceptions/unhandled-exception-info.interface.d.ts +15 -0
  22. package/dist/interfaces/exceptions/unhandled-exception-info.interface.js +2 -0
  23. package/dist/interfaces/exceptions/unhandled-exception-publisher.interface.d.ts +10 -0
  24. package/dist/interfaces/exceptions/unhandled-exception-publisher.interface.js +2 -0
  25. package/dist/interfaces/index.d.ts +2 -0
  26. package/dist/interfaces/index.js +2 -0
  27. package/dist/interfaces/queries/query-bus.interface.d.ts +7 -0
  28. package/dist/interfaces/queries/query-handler.interface.d.ts +7 -0
  29. package/dist/query-bus.d.ts +12 -0
  30. package/dist/query-bus.js +21 -20
  31. package/dist/unhandled-exception-bus.d.ts +35 -0
  32. package/dist/unhandled-exception-bus.js +65 -0
  33. package/package.json +14 -14
@@ -3,21 +3,21 @@ version: 2
3
3
  aliases:
4
4
  - &restore-cache
5
5
  restore_cache:
6
- key: dependency-cache-{{ checksum "package.json" }}
6
+ key: dependency-cache-{{ checksum "package.json" }}
7
7
  - &install-deps
8
8
  run:
9
- name: Install dependencies
10
- command: npm ci
9
+ name: Install dependencies
10
+ command: npm ci --ignore-scripts
11
11
  - &build-packages
12
12
  run:
13
- name: Build
14
- command: npm run build
13
+ name: Build
14
+ command: npm run build
15
15
 
16
16
  jobs:
17
17
  build:
18
18
  working_directory: ~/nest
19
19
  docker:
20
- - image: cimg/node:19.6
20
+ - image: cimg/node:20.3
21
21
  steps:
22
22
  - checkout
23
23
  - run:
@@ -27,18 +27,17 @@ jobs:
27
27
  key: dependency-cache-{{ checksum "package.json" }}
28
28
  - run:
29
29
  name: Install dependencies
30
- command: npm ci
30
+ command: npm ci --ignore-scripts
31
31
  - save_cache:
32
32
  key: dependency-cache-{{ checksum "package.json" }}
33
33
  paths:
34
34
  - ./node_modules
35
35
  - run:
36
36
  name: Build
37
- command: npm run build
37
+ command: npm run build
38
38
 
39
39
  workflows:
40
40
  version: 2
41
41
  build-and-test:
42
42
  jobs:
43
43
  - build
44
-
@@ -1,18 +1,63 @@
1
- import { IEvent, IEventHandler } from './interfaces';
2
1
  import { Type } from '@nestjs/common';
2
+ import { IEvent, IEventHandler } from './interfaces';
3
3
  declare const INTERNAL_EVENTS: unique symbol;
4
4
  declare const IS_AUTO_COMMIT_ENABLED: unique symbol;
5
+ /**
6
+ * Represents an aggregate root.
7
+ * An aggregate root is an entity that represents a meaningful concept in the domain.
8
+ * It is the root of an aggregate, which is a cluster of domain objects that can be treated as a single unit.
9
+ *
10
+ * @template EventBase The base type of the events.
11
+ */
5
12
  export declare abstract class AggregateRoot<EventBase extends IEvent = IEvent> {
6
13
  [IS_AUTO_COMMIT_ENABLED]: boolean;
7
14
  private readonly [INTERNAL_EVENTS];
15
+ /**
16
+ * Sets whether the aggregate root should automatically commit events.
17
+ */
8
18
  set autoCommit(value: boolean);
19
+ /**
20
+ * Gets whether the aggregate root should automatically commit events.
21
+ */
9
22
  get autoCommit(): boolean;
23
+ /**
24
+ * Publishes an event. Must be merged with the publisher context in order to work.
25
+ * @param event The event to publish.
26
+ */
10
27
  publish<T extends EventBase = EventBase>(event: T): void;
11
- publishAll<T extends EventBase = EventBase>(event: T[]): void;
28
+ /**
29
+ * Publishes multiple events. Must be merged with the publisher context in order to work.
30
+ * @param events The events to publish.
31
+ */
32
+ publishAll<T extends EventBase = EventBase>(events: T[]): void;
33
+ /**
34
+ * Commits all uncommitted events.
35
+ */
12
36
  commit(): void;
37
+ /**
38
+ * Uncommits all events.
39
+ */
13
40
  uncommit(): void;
41
+ /**
42
+ * Returns all uncommitted events.
43
+ * @returns All uncommitted events.
44
+ */
14
45
  getUncommittedEvents(): EventBase[];
46
+ /**
47
+ * Loads events from history.
48
+ * @param history The history to load.
49
+ */
15
50
  loadFromHistory(history: EventBase[]): void;
51
+ /**
52
+ * Applies an event.
53
+ * If auto commit is enabled, the event will be published immediately (note: must be merged with the publisher context in order to work).
54
+ * Otherwise, the event will be stored in the internal events array, and will be published when the commit method is called.
55
+ * Also, the corresponding event handler will be called (if exists).
56
+ * For example, if the event is called UserCreatedEvent, the "onUserCreatedEvent" method will be called.
57
+ *
58
+ * @param event The event to apply.
59
+ * @param isFromHistory Whether the event is from history.
60
+ */
16
61
  apply<T extends EventBase = EventBase>(event: T, isFromHistory?: boolean): void;
17
62
  protected getEventHandler<T extends EventBase = EventBase>(event: T): Type<IEventHandler> | undefined;
18
63
  protected getEventName(event: any): string;
@@ -4,32 +4,77 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.AggregateRoot = void 0;
5
5
  const INTERNAL_EVENTS = Symbol();
6
6
  const IS_AUTO_COMMIT_ENABLED = Symbol();
7
+ /**
8
+ * Represents an aggregate root.
9
+ * An aggregate root is an entity that represents a meaningful concept in the domain.
10
+ * It is the root of an aggregate, which is a cluster of domain objects that can be treated as a single unit.
11
+ *
12
+ * @template EventBase The base type of the events.
13
+ */
7
14
  class AggregateRoot {
8
15
  constructor() {
9
16
  this[_a] = false;
10
17
  this[_b] = [];
11
18
  }
19
+ /**
20
+ * Sets whether the aggregate root should automatically commit events.
21
+ */
12
22
  set autoCommit(value) {
13
23
  this[IS_AUTO_COMMIT_ENABLED] = value;
14
24
  }
25
+ /**
26
+ * Gets whether the aggregate root should automatically commit events.
27
+ */
15
28
  get autoCommit() {
16
29
  return this[IS_AUTO_COMMIT_ENABLED];
17
30
  }
31
+ /**
32
+ * Publishes an event. Must be merged with the publisher context in order to work.
33
+ * @param event The event to publish.
34
+ */
18
35
  publish(event) { }
19
- publishAll(event) { }
36
+ /**
37
+ * Publishes multiple events. Must be merged with the publisher context in order to work.
38
+ * @param events The events to publish.
39
+ */
40
+ publishAll(events) { }
41
+ /**
42
+ * Commits all uncommitted events.
43
+ */
20
44
  commit() {
21
45
  this.publishAll(this[INTERNAL_EVENTS]);
22
46
  this[INTERNAL_EVENTS].length = 0;
23
47
  }
48
+ /**
49
+ * Uncommits all events.
50
+ */
24
51
  uncommit() {
25
52
  this[INTERNAL_EVENTS].length = 0;
26
53
  }
54
+ /**
55
+ * Returns all uncommitted events.
56
+ * @returns All uncommitted events.
57
+ */
27
58
  getUncommittedEvents() {
28
59
  return this[INTERNAL_EVENTS];
29
60
  }
61
+ /**
62
+ * Loads events from history.
63
+ * @param history The history to load.
64
+ */
30
65
  loadFromHistory(history) {
31
66
  history.forEach((event) => this.apply(event, true));
32
67
  }
68
+ /**
69
+ * Applies an event.
70
+ * If auto commit is enabled, the event will be published immediately (note: must be merged with the publisher context in order to work).
71
+ * Otherwise, the event will be stored in the internal events array, and will be published when the commit method is called.
72
+ * Also, the corresponding event handler will be called (if exists).
73
+ * For example, if the event is called UserCreatedEvent, the "onUserCreatedEvent" method will be called.
74
+ *
75
+ * @param event The event to apply.
76
+ * @param isFromHistory Whether the event is from history.
77
+ */
33
78
  apply(event, isFromHistory = false) {
34
79
  if (!isFromHistory && !this.autoCommit) {
35
80
  this[INTERNAL_EVENTS].push(event);
@@ -9,8 +9,22 @@ export declare class CommandBus<CommandBase extends ICommand = ICommand> extends
9
9
  private handlers;
10
10
  private _publisher;
11
11
  constructor(moduleRef: ModuleRef);
12
+ /**
13
+ * Returns the publisher.
14
+ * Default publisher is `DefaultCommandPubSub` (in memory).
15
+ */
12
16
  get publisher(): ICommandPublisher<CommandBase>;
17
+ /**
18
+ * Sets the publisher.
19
+ * Default publisher is `DefaultCommandPubSub` (in memory).
20
+ * @param _publisher The publisher to set.
21
+ */
13
22
  set publisher(_publisher: ICommandPublisher<CommandBase>);
23
+ /**
24
+ * Executes a command.
25
+ * @param command The command to execute.
26
+ * @returns A promise that, when resolved, will contain the result returned by the command's handler.
27
+ */
14
28
  execute<T extends CommandBase, R = any>(command: T): Promise<R>;
15
29
  bind<T extends CommandBase>(handler: ICommandHandler<T>, id: string): void;
16
30
  register(handlers?: CommandHandlerType[]): void;
@@ -25,12 +25,26 @@ let CommandBus = class CommandBus extends observable_bus_1.ObservableBus {
25
25
  this.handlers = new Map();
26
26
  this.useDefaultPublisher();
27
27
  }
28
+ /**
29
+ * Returns the publisher.
30
+ * Default publisher is `DefaultCommandPubSub` (in memory).
31
+ */
28
32
  get publisher() {
29
33
  return this._publisher;
30
34
  }
35
+ /**
36
+ * Sets the publisher.
37
+ * Default publisher is `DefaultCommandPubSub` (in memory).
38
+ * @param _publisher The publisher to set.
39
+ */
31
40
  set publisher(_publisher) {
32
41
  this._publisher = _publisher;
33
42
  }
43
+ /**
44
+ * Executes a command.
45
+ * @param command The command to execute.
46
+ * @returns A promise that, when resolved, will contain the result returned by the command's handler.
47
+ */
34
48
  execute(command) {
35
49
  const commandId = this.getCommandId(command);
36
50
  const handler = this.handlers.get(commandId);
@@ -6,9 +6,9 @@ import { QueryBus } from './query-bus';
6
6
  import { ExplorerService } from './services/explorer.service';
7
7
  export declare class CqrsModule<EventBase extends IEvent = IEvent> implements OnApplicationBootstrap {
8
8
  private readonly explorerService;
9
- private readonly eventsBus;
10
- private readonly commandsBus;
9
+ private readonly eventBus;
10
+ private readonly commandBus;
11
11
  private readonly queryBus;
12
- constructor(explorerService: ExplorerService<EventBase>, eventsBus: EventBus<EventBase>, commandsBus: CommandBus, queryBus: QueryBus);
12
+ constructor(explorerService: ExplorerService<EventBase>, eventBus: EventBus<EventBase>, commandBus: CommandBus, queryBus: QueryBus);
13
13
  onApplicationBootstrap(): void;
14
14
  }
@@ -16,25 +16,39 @@ const event_bus_1 = require("./event-bus");
16
16
  const event_publisher_1 = require("./event-publisher");
17
17
  const query_bus_1 = require("./query-bus");
18
18
  const explorer_service_1 = require("./services/explorer.service");
19
+ const unhandled_exception_bus_1 = require("./unhandled-exception-bus");
19
20
  let CqrsModule = class CqrsModule {
20
- constructor(explorerService, eventsBus, commandsBus, queryBus) {
21
+ constructor(explorerService, eventBus, commandBus, queryBus) {
21
22
  this.explorerService = explorerService;
22
- this.eventsBus = eventsBus;
23
- this.commandsBus = commandsBus;
23
+ this.eventBus = eventBus;
24
+ this.commandBus = commandBus;
24
25
  this.queryBus = queryBus;
25
26
  }
26
27
  onApplicationBootstrap() {
27
28
  const { events, queries, sagas, commands } = this.explorerService.explore();
28
- this.eventsBus.register(events);
29
- this.commandsBus.register(commands);
29
+ this.eventBus.register(events);
30
+ this.commandBus.register(commands);
30
31
  this.queryBus.register(queries);
31
- this.eventsBus.registerSagas(sagas);
32
+ this.eventBus.registerSagas(sagas);
32
33
  }
33
34
  };
34
35
  CqrsModule = __decorate([
35
36
  (0, common_1.Module)({
36
- providers: [command_bus_1.CommandBus, query_bus_1.QueryBus, event_bus_1.EventBus, event_publisher_1.EventPublisher, explorer_service_1.ExplorerService],
37
- exports: [command_bus_1.CommandBus, query_bus_1.QueryBus, event_bus_1.EventBus, event_publisher_1.EventPublisher],
37
+ providers: [
38
+ command_bus_1.CommandBus,
39
+ query_bus_1.QueryBus,
40
+ event_bus_1.EventBus,
41
+ unhandled_exception_bus_1.UnhandledExceptionBus,
42
+ event_publisher_1.EventPublisher,
43
+ explorer_service_1.ExplorerService,
44
+ ],
45
+ exports: [
46
+ command_bus_1.CommandBus,
47
+ query_bus_1.QueryBus,
48
+ event_bus_1.EventBus,
49
+ unhandled_exception_bus_1.UnhandledExceptionBus,
50
+ event_publisher_1.EventPublisher,
51
+ ],
38
52
  }),
39
53
  __metadata("design:paramtypes", [explorer_service_1.ExplorerService,
40
54
  event_bus_1.EventBus,
@@ -3,20 +3,39 @@ import { ModuleRef } from '@nestjs/core';
3
3
  import { Observable, Subscription } from 'rxjs';
4
4
  import { CommandBus } from './command-bus';
5
5
  import { IEvent, IEventBus, IEventHandler, IEventPublisher, ISaga } from './interfaces';
6
+ import { UnhandledExceptionBus } from './unhandled-exception-bus';
6
7
  import { ObservableBus } from './utils';
7
8
  export declare type EventHandlerType<EventBase extends IEvent = IEvent> = Type<IEventHandler<EventBase>>;
8
9
  export declare class EventBus<EventBase extends IEvent = IEvent> extends ObservableBus<EventBase> implements IEventBus<EventBase>, OnModuleDestroy {
9
10
  private readonly commandBus;
10
11
  private readonly moduleRef;
12
+ private readonly unhandledExceptionBus;
11
13
  protected getEventId: (event: EventBase) => string | null;
12
14
  protected readonly subscriptions: Subscription[];
13
15
  private _publisher;
14
16
  private readonly _logger;
15
- constructor(commandBus: CommandBus, moduleRef: ModuleRef);
17
+ constructor(commandBus: CommandBus, moduleRef: ModuleRef, unhandledExceptionBus: UnhandledExceptionBus);
18
+ /**
19
+ * Returns the publisher.
20
+ * Default publisher is `DefaultPubSub` (in memory).
21
+ */
16
22
  get publisher(): IEventPublisher<EventBase>;
23
+ /**
24
+ * Sets the publisher.
25
+ * Default publisher is `DefaultPubSub` (in memory).
26
+ * @param _publisher The publisher to set.
27
+ */
17
28
  set publisher(_publisher: IEventPublisher<EventBase>);
18
29
  onModuleDestroy(): void;
30
+ /**
31
+ * Publishes an event.
32
+ * @param event The event to publish.
33
+ */
19
34
  publish<T extends EventBase>(event: T): any;
35
+ /**
36
+ * Publishes multiple events.
37
+ * @param events The events to publish.
38
+ */
20
39
  publishAll<T extends EventBase>(events: T[]): any;
21
40
  bind(handler: IEventHandler<EventBase>, id: string): void;
22
41
  registerSagas(types?: Type<unknown>[]): void;
@@ -26,4 +45,6 @@ export declare class EventBus<EventBase extends IEvent = IEvent> extends Observa
26
45
  protected registerSaga(saga: ISaga<EventBase>): void;
27
46
  private reflectEvents;
28
47
  private useDefaultPublisher;
48
+ private mapToUnhandledErrorInfo;
49
+ private isUnhandledErrorInfo;
29
50
  }
package/dist/event-bus.js CHANGED
@@ -15,35 +15,53 @@ const common_1 = require("@nestjs/common");
15
15
  const core_1 = require("@nestjs/core");
16
16
  const rxjs_1 = require("rxjs");
17
17
  const operators_1 = require("rxjs/operators");
18
- const util_1 = require("util");
19
18
  const command_bus_1 = require("./command-bus");
20
19
  const constants_1 = require("./decorators/constants");
21
20
  const exceptions_1 = require("./exceptions");
22
21
  const default_get_event_id_1 = require("./helpers/default-get-event-id");
23
22
  const default_pubsub_1 = require("./helpers/default-pubsub");
23
+ const unhandled_exception_bus_1 = require("./unhandled-exception-bus");
24
24
  const utils_1 = require("./utils");
25
25
  let EventBus = EventBus_1 = class EventBus extends utils_1.ObservableBus {
26
- constructor(commandBus, moduleRef) {
26
+ constructor(commandBus, moduleRef, unhandledExceptionBus) {
27
27
  super();
28
28
  this.commandBus = commandBus;
29
29
  this.moduleRef = moduleRef;
30
+ this.unhandledExceptionBus = unhandledExceptionBus;
30
31
  this._logger = new common_1.Logger(EventBus_1.name);
31
32
  this.subscriptions = [];
32
33
  this.getEventId = default_get_event_id_1.defaultGetEventId;
33
34
  this.useDefaultPublisher();
34
35
  }
36
+ /**
37
+ * Returns the publisher.
38
+ * Default publisher is `DefaultPubSub` (in memory).
39
+ */
35
40
  get publisher() {
36
41
  return this._publisher;
37
42
  }
43
+ /**
44
+ * Sets the publisher.
45
+ * Default publisher is `DefaultPubSub` (in memory).
46
+ * @param _publisher The publisher to set.
47
+ */
38
48
  set publisher(_publisher) {
39
49
  this._publisher = _publisher;
40
50
  }
41
51
  onModuleDestroy() {
42
52
  this.subscriptions.forEach((subscription) => subscription.unsubscribe());
43
53
  }
54
+ /**
55
+ * Publishes an event.
56
+ * @param event The event to publish.
57
+ */
44
58
  publish(event) {
45
59
  return this._publisher.publish(event);
46
60
  }
61
+ /**
62
+ * Publishes multiple events.
63
+ * @param events The events to publish.
64
+ */
47
65
  publishAll(events) {
48
66
  if (this._publisher.publishAll) {
49
67
  return this._publisher.publishAll(events);
@@ -53,11 +71,14 @@ let EventBus = EventBus_1 = class EventBus extends utils_1.ObservableBus {
53
71
  bind(handler, id) {
54
72
  const stream$ = id ? this.ofEventId(id) : this.subject$;
55
73
  const subscription = stream$
56
- .pipe((0, operators_1.mergeMap)((event) => (0, rxjs_1.from)(Promise.resolve(handler.handle(event)))))
74
+ .pipe((0, operators_1.mergeMap)((event) => (0, rxjs_1.defer)(() => Promise.resolve(handler.handle(event))).pipe((0, operators_1.catchError)((error) => (0, rxjs_1.throwError)(() => this.mapToUnhandledErrorInfo(event, error))))))
57
75
  .subscribe({
58
76
  error: (error) => {
59
- this._logger.error(`"${handler.constructor.name}" has thrown an error.`, error);
60
- throw error;
77
+ if (this.isUnhandledErrorInfo(error)) {
78
+ this.unhandledExceptionBus.publish(error);
79
+ error = error.exception;
80
+ }
81
+ this._logger.error(`"${handler.constructor.name}" has thrown an unhandled exception.`, error);
61
82
  },
62
83
  });
63
84
  this.subscriptions.push(subscription);
@@ -90,7 +111,7 @@ let EventBus = EventBus_1 = class EventBus extends utils_1.ObservableBus {
90
111
  return this.subject$.pipe((0, operators_1.filter)((event) => this.getEventId(event) === id));
91
112
  }
92
113
  registerSaga(saga) {
93
- if (!(0, util_1.isFunction)(saga)) {
114
+ if (typeof saga !== 'function') {
94
115
  throw new exceptions_1.InvalidSagaException();
95
116
  }
96
117
  const stream$ = saga(this);
@@ -98,11 +119,14 @@ let EventBus = EventBus_1 = class EventBus extends utils_1.ObservableBus {
98
119
  throw new exceptions_1.InvalidSagaException();
99
120
  }
100
121
  const subscription = stream$
101
- .pipe((0, operators_1.filter)((e) => !!e), (0, operators_1.mergeMap)((command) => (0, rxjs_1.from)(this.commandBus.execute(command))))
122
+ .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) => (0, rxjs_1.throwError)(() => this.mapToUnhandledErrorInfo(command, error))))))
102
123
  .subscribe({
103
124
  error: (error) => {
104
- this._logger.error(`Command handler which execution was triggered by Saga has thrown an error.`, error);
105
- throw error;
125
+ if (this.isUnhandledErrorInfo(error)) {
126
+ this.unhandledExceptionBus.publish(error);
127
+ error = error.exception;
128
+ }
129
+ this._logger.error(`Command handler which execution was triggered by Saga has thrown an unhandled exception.`, error);
106
130
  },
107
131
  });
108
132
  this.subscriptions.push(subscription);
@@ -113,10 +137,23 @@ let EventBus = EventBus_1 = class EventBus extends utils_1.ObservableBus {
113
137
  useDefaultPublisher() {
114
138
  this._publisher = new default_pubsub_1.DefaultPubSub(this.subject$);
115
139
  }
140
+ mapToUnhandledErrorInfo(eventOrCommand, exception) {
141
+ return {
142
+ cause: eventOrCommand,
143
+ exception,
144
+ };
145
+ }
146
+ isUnhandledErrorInfo(error) {
147
+ return (typeof error === 'object' &&
148
+ error !== null &&
149
+ 'cause' in error &&
150
+ 'exception' in error);
151
+ }
116
152
  };
117
153
  EventBus = EventBus_1 = __decorate([
118
154
  (0, common_1.Injectable)(),
119
155
  __metadata("design:paramtypes", [command_bus_1.CommandBus,
120
- core_1.ModuleRef])
156
+ core_1.ModuleRef,
157
+ unhandled_exception_bus_1.UnhandledExceptionBus])
121
158
  ], EventBus);
122
159
  exports.EventBus = EventBus;
@@ -1,5 +1,5 @@
1
- import { EventBus } from './event-bus';
2
1
  import { AggregateRoot } from './aggregate-root';
2
+ import { EventBus } from './event-bus';
3
3
  import { IEvent } from './interfaces';
4
4
  export interface Constructor<T> {
5
5
  new (...args: any[]): T;
@@ -7,6 +7,16 @@ export interface Constructor<T> {
7
7
  export declare class EventPublisher<EventBase extends IEvent = IEvent> {
8
8
  private readonly eventBus;
9
9
  constructor(eventBus: EventBus<EventBase>);
10
+ /**
11
+ * Merge the event publisher into the provided class.
12
+ * This is required to make `publish` and `publishAll` available on the `AgreggateRoot` class.
13
+ * @param metatype The class to merge into.
14
+ */
10
15
  mergeClassContext<T extends Constructor<AggregateRoot<EventBase>>>(metatype: T): T;
16
+ /**
17
+ * Merge the event publisher into the provided object.
18
+ * This is required to make `publish` and `publishAll` available on the `AgreggateRoot` class instance.
19
+ * @param object The object to merge into.
20
+ */
11
21
  mergeObjectContext<T extends AggregateRoot<EventBase>>(object: T): T;
12
22
  }
@@ -16,6 +16,11 @@ let EventPublisher = class EventPublisher {
16
16
  constructor(eventBus) {
17
17
  this.eventBus = eventBus;
18
18
  }
19
+ /**
20
+ * Merge the event publisher into the provided class.
21
+ * This is required to make `publish` and `publishAll` available on the `AgreggateRoot` class.
22
+ * @param metatype The class to merge into.
23
+ */
19
24
  mergeClassContext(metatype) {
20
25
  const eventBus = this.eventBus;
21
26
  return class extends metatype {
@@ -27,6 +32,11 @@ let EventPublisher = class EventPublisher {
27
32
  }
28
33
  };
29
34
  }
35
+ /**
36
+ * Merge the event publisher into the provided object.
37
+ * This is required to make `publish` and `publishAll` available on the `AgreggateRoot` class instance.
38
+ * @param object The object to merge into.
39
+ */
30
40
  mergeObjectContext(object) {
31
41
  const eventBus = this.eventBus;
32
42
  object.publish = (event) => {
@@ -8,9 +8,8 @@ const constants_1 = require("../decorators/constants");
8
8
  * @returns
9
9
  */
10
10
  const defaultGetEventId = (event) => {
11
- var _a, _b;
12
11
  const { constructor } = Object.getPrototypeOf(event);
13
- return (_b = (_a = Reflect.getMetadata(constants_1.EVENT_METADATA, constructor)) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : null;
12
+ return Reflect.getMetadata(constants_1.EVENT_METADATA, constructor)?.id ?? null;
14
13
  };
15
14
  exports.defaultGetEventId = defaultGetEventId;
16
15
  const defaultReflectEventId = (event) => {
@@ -0,0 +1,10 @@
1
+ import { Subject } from 'rxjs';
2
+ import { ICommand, IEvent, IUnhandledExceptionPublisher, UnhandledExceptionInfo } from '../interfaces';
3
+ /**
4
+ * Default implementation of the `IUnhandledExceptionPublisher` interface.
5
+ */
6
+ export declare class DefaultUnhandledExceptionPubSub<Cause = IEvent | ICommand> implements IUnhandledExceptionPublisher<Cause> {
7
+ private subject$;
8
+ constructor(subject$: Subject<UnhandledExceptionInfo<Cause>>);
9
+ publish(info: UnhandledExceptionInfo<Cause>): void;
10
+ }
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DefaultUnhandledExceptionPubSub = void 0;
4
+ /**
5
+ * Default implementation of the `IUnhandledExceptionPublisher` interface.
6
+ */
7
+ class DefaultUnhandledExceptionPubSub {
8
+ constructor(subject$) {
9
+ this.subject$ = subject$;
10
+ }
11
+ publish(info) {
12
+ this.subject$.next(info);
13
+ }
14
+ }
15
+ exports.DefaultUnhandledExceptionPubSub = DefaultUnhandledExceptionPubSub;
package/dist/index.d.ts CHANGED
@@ -8,4 +8,5 @@ export * from './exceptions';
8
8
  export * from './interfaces';
9
9
  export * from './operators';
10
10
  export * from './query-bus';
11
+ export * from './unhandled-exception-bus';
11
12
  export * from './utils';
package/dist/index.js CHANGED
@@ -26,4 +26,5 @@ __exportStar(require("./exceptions"), exports);
26
26
  __exportStar(require("./interfaces"), exports);
27
27
  __exportStar(require("./operators"), exports);
28
28
  __exportStar(require("./query-bus"), exports);
29
+ __exportStar(require("./unhandled-exception-bus"), exports);
29
30
  __exportStar(require("./utils"), exports);
@@ -1,4 +1,11 @@
1
1
  import { ICommand } from './command.interface';
2
+ /**
3
+ * Represents a command bus.
4
+ */
2
5
  export interface ICommandBus<CommandBase extends ICommand = ICommand> {
6
+ /**
7
+ * Executes a command.
8
+ * @param command The command to execute.
9
+ */
3
10
  execute<T extends CommandBase, R = any>(command: T): Promise<R>;
4
11
  }
@@ -1,4 +1,12 @@
1
1
  import { ICommand } from './command.interface';
2
+ /**
3
+ * Represents a command handler.
4
+ * Command handlers are used to execute commands.
5
+ */
2
6
  export interface ICommandHandler<TCommand extends ICommand = any, TResult = any> {
7
+ /**
8
+ * Executes a command.
9
+ * @param command The command to execute.
10
+ */
3
11
  execute(command: TCommand): Promise<TResult>;
4
12
  }
@@ -1,5 +1,16 @@
1
1
  import { IEvent } from './event.interface';
2
+ /**
3
+ * Represents an event bus.
4
+ */
2
5
  export interface IEventBus<EventBase extends IEvent = IEvent> {
6
+ /**
7
+ * Publishes an event.
8
+ * @param event The event to publish.
9
+ */
3
10
  publish<T extends EventBase>(event: T): any;
11
+ /**
12
+ * Publishes multiple events.
13
+ * @param events The events to publish.
14
+ */
4
15
  publishAll(events: EventBase[]): any;
5
16
  }
@@ -1,4 +1,11 @@
1
1
  import { IEvent } from './event.interface';
2
+ /**
3
+ * Represents an event handler.
4
+ */
2
5
  export interface IEventHandler<T extends IEvent = any> {
6
+ /**
7
+ * Handles an event.
8
+ * @param event The event to handle.
9
+ */
3
10
  handle(event: T): any;
4
11
  }
@@ -0,0 +1,15 @@
1
+ import { ICommand } from '../commands/command.interface';
2
+ import { IEvent } from '../events/event.interface';
3
+ /**
4
+ * Represents an unhandled exception.
5
+ */
6
+ export interface UnhandledExceptionInfo<Cause = IEvent | ICommand, Exception = any> {
7
+ /**
8
+ * The exception that was thrown.
9
+ */
10
+ exception: Exception;
11
+ /**
12
+ * The cause of the exception (event or command reference).
13
+ */
14
+ cause: Cause;
15
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,10 @@
1
+ import { ICommand } from '../commands/command.interface';
2
+ import { IEvent } from '../events/event.interface';
3
+ import { UnhandledExceptionInfo } from './unhandled-exception-info.interface';
4
+ export interface IUnhandledExceptionPublisher<CauseBase = IEvent | ICommand, ExceptionBase = any> {
5
+ /**
6
+ * Publishes an unhandled exception.
7
+ * @param info The exception information.
8
+ */
9
+ publish(info: UnhandledExceptionInfo<CauseBase, ExceptionBase>): any;
10
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -7,6 +7,8 @@ export * from './events/event-handler.interface';
7
7
  export * from './events/event-publisher.interface';
8
8
  export * from './events/event.interface';
9
9
  export * from './events/message-source.interface';
10
+ export * from './exceptions/unhandled-exception-info.interface';
11
+ export * from './exceptions/unhandled-exception-publisher.interface';
10
12
  export * from './queries/query-bus.interface';
11
13
  export * from './queries/query-handler.interface';
12
14
  export * from './queries/query-publisher.interface';
@@ -19,6 +19,8 @@ __exportStar(require("./events/event-handler.interface"), exports);
19
19
  __exportStar(require("./events/event-publisher.interface"), exports);
20
20
  __exportStar(require("./events/event.interface"), exports);
21
21
  __exportStar(require("./events/message-source.interface"), exports);
22
+ __exportStar(require("./exceptions/unhandled-exception-info.interface"), exports);
23
+ __exportStar(require("./exceptions/unhandled-exception-publisher.interface"), exports);
22
24
  __exportStar(require("./queries/query-bus.interface"), exports);
23
25
  __exportStar(require("./queries/query-handler.interface"), exports);
24
26
  __exportStar(require("./queries/query-publisher.interface"), exports);
@@ -1,4 +1,11 @@
1
1
  import { IQuery } from './query.interface';
2
+ /**
3
+ * Represents a query bus.
4
+ */
2
5
  export interface IQueryBus<QueryBase extends IQuery = IQuery> {
6
+ /**
7
+ * Executes a query.
8
+ * @param query The query to execute.
9
+ */
3
10
  execute<T extends QueryBase = QueryBase, TRes = any>(query: T): Promise<TRes>;
4
11
  }
@@ -1,4 +1,11 @@
1
1
  import { IQuery } from './query.interface';
2
+ /**
3
+ * Represents a query handler.
4
+ */
2
5
  export interface IQueryHandler<T extends IQuery = any, TRes = any> {
6
+ /**
7
+ * Executes a query.
8
+ * @param query The query to execute.
9
+ */
3
10
  execute(query: T): Promise<TRes>;
4
11
  }
@@ -9,8 +9,20 @@ export declare class QueryBus<QueryBase extends IQuery = IQuery> extends Observa
9
9
  private handlers;
10
10
  private _publisher;
11
11
  constructor(moduleRef: ModuleRef);
12
+ /**
13
+ * Returns the publisher.
14
+ */
12
15
  get publisher(): IQueryPublisher<QueryBase>;
16
+ /**
17
+ * Sets the publisher.
18
+ * Default publisher is `DefaultQueryPubSub` (in memory).
19
+ * @param _publisher The publisher to set.
20
+ */
13
21
  set publisher(_publisher: IQueryPublisher<QueryBase>);
22
+ /**
23
+ * Executes a query.
24
+ * @param query The query to execute.
25
+ */
14
26
  execute<T extends QueryBase, TResult = any>(query: T): Promise<TResult>;
15
27
  bind<T extends QueryBase, TResult = any>(handler: IQueryHandler<T, TResult>, queryId: string): void;
16
28
  register(handlers?: QueryHandlerType<QueryBase>[]): void;
package/dist/query-bus.js CHANGED
@@ -8,15 +8,6 @@ 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 __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
12
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
13
- return new (P || (P = Promise))(function (resolve, reject) {
14
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
15
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
16
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
17
- step((generator = generator.apply(thisArg, _arguments || [])).next());
18
- });
19
- };
20
11
  Object.defineProperty(exports, "__esModule", { value: true });
21
12
  exports.QueryBus = void 0;
22
13
  const common_1 = require("@nestjs/common");
@@ -34,23 +25,33 @@ let QueryBus = class QueryBus extends observable_bus_1.ObservableBus {
34
25
  this.handlers = new Map();
35
26
  this.useDefaultPublisher();
36
27
  }
28
+ /**
29
+ * Returns the publisher.
30
+ */
37
31
  get publisher() {
38
32
  return this._publisher;
39
33
  }
34
+ /**
35
+ * Sets the publisher.
36
+ * Default publisher is `DefaultQueryPubSub` (in memory).
37
+ * @param _publisher The publisher to set.
38
+ */
40
39
  set publisher(_publisher) {
41
40
  this._publisher = _publisher;
42
41
  }
43
- execute(query) {
44
- return __awaiter(this, void 0, void 0, function* () {
45
- const queryId = this.getQueryId(query);
46
- const handler = this.handlers.get(queryId);
47
- if (!handler) {
48
- throw new exceptions_1.QueryHandlerNotFoundException(queryId);
49
- }
50
- this._publisher.publish(query);
51
- const result = yield handler.execute(query);
52
- return result;
53
- });
42
+ /**
43
+ * Executes a query.
44
+ * @param query The query to execute.
45
+ */
46
+ async execute(query) {
47
+ const queryId = this.getQueryId(query);
48
+ const handler = this.handlers.get(queryId);
49
+ if (!handler) {
50
+ throw new exceptions_1.QueryHandlerNotFoundException(queryId);
51
+ }
52
+ this._publisher.publish(query);
53
+ const result = await handler.execute(query);
54
+ return result;
54
55
  }
55
56
  bind(handler, queryId) {
56
57
  this.handlers.set(queryId, handler);
@@ -0,0 +1,35 @@
1
+ import { Type } from '@nestjs/common';
2
+ import 'reflect-metadata';
3
+ import { Observable } from 'rxjs';
4
+ import { ICommand, IEvent, IUnhandledExceptionPublisher, UnhandledExceptionInfo } from './interfaces';
5
+ import { ObservableBus } from './utils/observable-bus';
6
+ /**
7
+ * A bus that publishes unhandled exceptions.
8
+ * @template Cause The type of the cause of the exception.
9
+ */
10
+ export declare class UnhandledExceptionBus<Cause = IEvent | ICommand> extends ObservableBus<UnhandledExceptionInfo<Cause>> {
11
+ private _publisher;
12
+ constructor();
13
+ /**
14
+ * Filter values depending on their instance type (comparison is made
15
+ * using native `instanceof`).
16
+ *
17
+ * @param types List of types to filter by.
18
+ * @return A stream only emitting the filtered exceptions.
19
+ */
20
+ static ofType<TInput, TOutput>(...types: Type<TOutput>[]): (source: Observable<UnhandledExceptionInfo<TInput>>) => Observable<UnhandledExceptionInfo<TOutput>>;
21
+ /**
22
+ * Gets the publisher of the bus.
23
+ */
24
+ get publisher(): IUnhandledExceptionPublisher<Cause>;
25
+ /**
26
+ * Sets the publisher of the bus.
27
+ */
28
+ set publisher(_publisher: IUnhandledExceptionPublisher<Cause>);
29
+ /**
30
+ * Publishes an unhandled exception.
31
+ * @param info The exception information.
32
+ */
33
+ publish(info: UnhandledExceptionInfo<Cause>): any;
34
+ private useDefaultPublisher;
35
+ }
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.UnhandledExceptionBus = void 0;
13
+ const common_1 = require("@nestjs/common");
14
+ require("reflect-metadata");
15
+ const rxjs_1 = require("rxjs");
16
+ const default_unhandled_exception_pubsub_1 = require("./helpers/default-unhandled-exception-pubsub");
17
+ const observable_bus_1 = require("./utils/observable-bus");
18
+ /**
19
+ * A bus that publishes unhandled exceptions.
20
+ * @template Cause The type of the cause of the exception.
21
+ */
22
+ let UnhandledExceptionBus = class UnhandledExceptionBus extends observable_bus_1.ObservableBus {
23
+ constructor() {
24
+ super();
25
+ this.useDefaultPublisher();
26
+ }
27
+ /**
28
+ * Filter values depending on their instance type (comparison is made
29
+ * using native `instanceof`).
30
+ *
31
+ * @param types List of types to filter by.
32
+ * @return A stream only emitting the filtered exceptions.
33
+ */
34
+ static ofType(...types) {
35
+ const isInstanceOf = (exceptionInfo) => !!types.find((classType) => exceptionInfo.exception instanceof classType);
36
+ return (source) => source.pipe((0, rxjs_1.filter)(isInstanceOf));
37
+ }
38
+ /**
39
+ * Gets the publisher of the bus.
40
+ */
41
+ get publisher() {
42
+ return this._publisher;
43
+ }
44
+ /**
45
+ * Sets the publisher of the bus.
46
+ */
47
+ set publisher(_publisher) {
48
+ this._publisher = _publisher;
49
+ }
50
+ /**
51
+ * Publishes an unhandled exception.
52
+ * @param info The exception information.
53
+ */
54
+ publish(info) {
55
+ return this._publisher.publish(info);
56
+ }
57
+ useDefaultPublisher() {
58
+ this._publisher = new default_unhandled_exception_pubsub_1.DefaultUnhandledExceptionPubSub(this.subject$);
59
+ }
60
+ };
61
+ UnhandledExceptionBus = __decorate([
62
+ (0, common_1.Injectable)(),
63
+ __metadata("design:paramtypes", [])
64
+ ], UnhandledExceptionBus);
65
+ exports.UnhandledExceptionBus = UnhandledExceptionBus;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nestjs/cqrs",
3
- "version": "9.0.4",
3
+ "version": "10.0.1",
4
4
  "description": "A lightweight CQRS module for Nest framework (node.js)",
5
5
  "license": "MIT",
6
6
  "url": "https://github.com/nestjs/cqrs#readme",
@@ -21,15 +21,15 @@
21
21
  "uuid": "9.0.0"
22
22
  },
23
23
  "devDependencies": {
24
- "@commitlint/cli": "17.6.3",
25
- "@commitlint/config-angular": "17.6.3",
26
- "@nestjs/common": "9.4.1",
27
- "@nestjs/core": "9.4.1",
28
- "@types/node": "18.16.12",
29
- "@types/jest": "29.5.1",
30
- "@typescript-eslint/eslint-plugin": "5.59.6",
31
- "@typescript-eslint/parser": "5.59.6",
32
- "eslint": "8.40.0",
24
+ "@commitlint/cli": "17.6.5",
25
+ "@commitlint/config-angular": "17.6.5",
26
+ "@nestjs/common": "10.0.3",
27
+ "@nestjs/core": "10.0.3",
28
+ "@types/node": "18.16.18",
29
+ "@types/jest": "29.5.2",
30
+ "@typescript-eslint/eslint-plugin": "5.60.0",
31
+ "@typescript-eslint/parser": "5.60.0",
32
+ "eslint": "8.43.0",
33
33
  "eslint-config-prettier": "8.8.0",
34
34
  "eslint-plugin-import": "2.27.5",
35
35
  "husky": "8.0.3",
@@ -37,14 +37,14 @@
37
37
  "lint-staged": "13.2.2",
38
38
  "prettier": "2.8.8",
39
39
  "reflect-metadata": "0.1.13",
40
- "release-it": "15.10.3",
40
+ "release-it": "15.11.0",
41
41
  "rxjs": "7.8.1",
42
42
  "ts-jest": "29.1.0",
43
- "typescript": "5.0.4"
43
+ "typescript": "5.1.3"
44
44
  },
45
45
  "peerDependencies": {
46
- "@nestjs/common": "^9.0.0",
47
- "@nestjs/core": "^9.0.0",
46
+ "@nestjs/common": "^9.0.0 || ^10.0.0",
47
+ "@nestjs/core": "^9.0.0 || ^10.0.0",
48
48
  "reflect-metadata": "0.1.13",
49
49
  "rxjs": "^7.2.0"
50
50
  },