node-cqrs 0.16.4 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/README.md +2 -1
  3. package/dist/AbstractAggregate.js +178 -0
  4. package/dist/AbstractAggregate.js.map +1 -0
  5. package/dist/AbstractProjection.js +121 -0
  6. package/dist/AbstractProjection.js.map +1 -0
  7. package/dist/AbstractSaga.js +99 -0
  8. package/dist/AbstractSaga.js.map +1 -0
  9. package/dist/AggregateCommandHandler.js +85 -0
  10. package/dist/AggregateCommandHandler.js.map +1 -0
  11. package/dist/CommandBus.js +77 -0
  12. package/dist/CommandBus.js.map +1 -0
  13. package/dist/CqrsContainerBuilder.js +77 -0
  14. package/dist/CqrsContainerBuilder.js.map +1 -0
  15. package/dist/Event.js +43 -0
  16. package/dist/Event.js.map +1 -0
  17. package/dist/EventStore.js +229 -0
  18. package/dist/EventStore.js.map +1 -0
  19. package/dist/SagaEventHandler.js +117 -0
  20. package/dist/SagaEventHandler.js.map +1 -0
  21. package/dist/index.js +39 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/infrastructure/InMemoryEventStorage.js +53 -0
  24. package/dist/infrastructure/InMemoryEventStorage.js.map +1 -0
  25. package/dist/infrastructure/InMemoryLock.js +68 -0
  26. package/dist/infrastructure/InMemoryLock.js.map +1 -0
  27. package/dist/infrastructure/InMemoryMessageBus.js +95 -0
  28. package/dist/infrastructure/InMemoryMessageBus.js.map +1 -0
  29. package/dist/infrastructure/InMemorySnapshotStorage.js +26 -0
  30. package/dist/infrastructure/InMemorySnapshotStorage.js.map +1 -0
  31. package/dist/infrastructure/InMemoryView.js +173 -0
  32. package/dist/infrastructure/InMemoryView.js.map +1 -0
  33. package/dist/infrastructure/utils/Deferred.js +38 -0
  34. package/dist/infrastructure/utils/Deferred.js.map +1 -0
  35. package/dist/infrastructure/utils/index.js +19 -0
  36. package/dist/infrastructure/utils/index.js.map +1 -0
  37. package/dist/infrastructure/utils/nextCycle.js +9 -0
  38. package/dist/infrastructure/utils/nextCycle.js.map +1 -0
  39. package/dist/interfaces.js +4 -0
  40. package/dist/interfaces.js.map +1 -0
  41. package/dist/utils/getClassName.js +10 -0
  42. package/dist/utils/getClassName.js.map +1 -0
  43. package/dist/utils/getHandledMessageTypes.js +18 -0
  44. package/dist/utils/getHandledMessageTypes.js.map +1 -0
  45. package/dist/utils/getHandler.js +20 -0
  46. package/dist/utils/getHandler.js.map +1 -0
  47. package/dist/utils/getMessageHandlerNames.js +38 -0
  48. package/dist/utils/getMessageHandlerNames.js.map +1 -0
  49. package/dist/utils/index.js +25 -0
  50. package/dist/utils/index.js.map +1 -0
  51. package/dist/utils/isClass.js +8 -0
  52. package/dist/utils/isClass.js.map +1 -0
  53. package/dist/utils/setupOneTimeEmitterSubscription.js +46 -0
  54. package/dist/utils/setupOneTimeEmitterSubscription.js.map +1 -0
  55. package/dist/utils/subscribe.js +39 -0
  56. package/dist/utils/subscribe.js.map +1 -0
  57. package/dist/utils/validateHandlers.js +21 -0
  58. package/dist/utils/validateHandlers.js.map +1 -0
  59. package/package.json +23 -12
  60. package/src/AbstractAggregate.ts +223 -0
  61. package/src/AbstractProjection.ts +172 -0
  62. package/src/AbstractSaga.ts +118 -0
  63. package/src/AggregateCommandHandler.ts +129 -0
  64. package/src/CommandBus.ts +98 -0
  65. package/src/CqrsContainerBuilder.ts +120 -0
  66. package/src/Event.ts +43 -0
  67. package/src/EventStore.ts +315 -0
  68. package/src/SagaEventHandler.ts +161 -0
  69. package/src/index.ts +26 -0
  70. package/src/infrastructure/InMemoryEventStorage.ts +68 -0
  71. package/src/infrastructure/InMemoryLock.ts +73 -0
  72. package/src/infrastructure/InMemoryMessageBus.ts +118 -0
  73. package/src/infrastructure/InMemorySnapshotStorage.ts +27 -0
  74. package/src/infrastructure/InMemoryView.ts +221 -0
  75. package/src/infrastructure/utils/Deferred.ts +41 -0
  76. package/src/infrastructure/utils/index.ts +2 -0
  77. package/src/infrastructure/utils/nextCycle.ts +4 -0
  78. package/src/interfaces.ts +328 -0
  79. package/src/utils/getClassName.ts +6 -0
  80. package/src/utils/{getHandledMessageTypes.js → getHandledMessageTypes.ts} +4 -8
  81. package/src/utils/{getHandler.js → getHandler.ts} +6 -7
  82. package/src/utils/{getMessageHandlerNames.js → getMessageHandlerNames.ts} +2 -9
  83. package/src/utils/index.ts +8 -0
  84. package/src/utils/{isClass.js → isClass.ts} +2 -4
  85. package/src/utils/setupOneTimeEmitterSubscription.ts +57 -0
  86. package/src/{subscribe.js → utils/subscribe.ts} +21 -18
  87. package/src/utils/{validateHandlers.js → validateHandlers.ts} +2 -8
  88. package/jsconfig.json +0 -15
  89. package/src/AbstractAggregate.js +0 -277
  90. package/src/AbstractProjection.js +0 -192
  91. package/src/AbstractSaga.js +0 -171
  92. package/src/AggregateCommandHandler.js +0 -126
  93. package/src/CommandBus.js +0 -91
  94. package/src/CqrsContainerBuilder.js +0 -131
  95. package/src/EventStore.js +0 -457
  96. package/src/EventStream.js +0 -63
  97. package/src/SagaEventHandler.js +0 -141
  98. package/src/index.js +0 -21
  99. package/src/infrastructure/InMemoryEventStorage.js +0 -76
  100. package/src/infrastructure/InMemoryMessageBus.js +0 -132
  101. package/src/infrastructure/InMemorySnapshotStorage.js +0 -40
  102. package/src/infrastructure/InMemoryView.js +0 -265
  103. package/src/utils/getClassName.js +0 -11
  104. package/src/utils/index.js +0 -6
  105. package/src/utils/nullLogger.js +0 -8
  106. package/types/index.d.ts +0 -16
  107. package/types/interfaces/IAggregate.d.ts +0 -30
  108. package/types/interfaces/IAggregateSnapshotStorage.d.ts +0 -4
  109. package/types/interfaces/ICommandBus.d.ts +0 -6
  110. package/types/interfaces/ICommandHandler.d.ts +0 -3
  111. package/types/interfaces/IConcurrentView.d.ts +0 -22
  112. package/types/interfaces/IEventReceptor.d.ts +0 -3
  113. package/types/interfaces/IEventStorage.d.ts +0 -20
  114. package/types/interfaces/IEventStore.d.ts +0 -18
  115. package/types/interfaces/IEventStream.d.ts +0 -13
  116. package/types/interfaces/ILogger.d.ts +0 -3
  117. package/types/interfaces/IMessageBus.d.ts +0 -5
  118. package/types/interfaces/IObserver.d.ts +0 -11
  119. package/types/interfaces/IProjection.d.ts +0 -10
  120. package/types/interfaces/ISaga.d.ts +0 -27
  121. package/types/interfaces/Identifier.d.ts +0 -1
package/CHANGELOG.md CHANGED
@@ -1,3 +1,48 @@
1
+ # [0.17.0](https://github.com/snatalenko/node-cqrs/compare/v1.0.0-rc.5...v0.17.0) (2025-08-12)
2
+
3
+
4
+
5
+ # [1.0.0-rc.5](https://github.com/snatalenko/node-cqrs/compare/v1.0.0-rc.4...v1.0.0-rc.5) (2024-10-27)
6
+
7
+
8
+ ### Changes
9
+
10
+ * Add `InMemoryView.prototype.getSync` method ([5d4adb9](https://github.com/snatalenko/node-cqrs/commit/5d4adb9109c4c85edae2b0f3dfd995e8c51aef06))
11
+
12
+
13
+ # [1.0.0-rc.4](https://github.com/snatalenko/node-cqrs/compare/v1.0.0-rc.3...v1.0.0-rc.4) (2024-10-02)
14
+
15
+
16
+
17
+ # [1.0.0-rc.3](https://github.com/snatalenko/node-cqrs/compare/v1.0.0-rc.2...v1.0.0-rc.3) (2024-09-23)
18
+
19
+
20
+
21
+ # [1.0.0-rc.2](https://github.com/snatalenko/node-cqrs/compare/v1.0.0-rc.1...v1.0.0-rc.2) (2024-08-03)
22
+
23
+
24
+
25
+ # [1.0.0-rc.1](https://github.com/snatalenko/node-cqrs/compare/v1.0.0-rc.0...v1.0.0-rc.1) (2024-08-03)
26
+
27
+
28
+ ### Build System
29
+
30
+ * Add NPM publishing script ([3372990](https://github.com/snatalenko/node-cqrs/commit/3372990ba2549695398e0949e35009396e660005))
31
+ * Suppress audit and test for tags ([574a00c](https://github.com/snatalenko/node-cqrs/commit/574a00cc53af009994ca4dd3278cb764743b4ad6))
32
+
33
+
34
+ # [1.0.0-rc.0](https://github.com/snatalenko/node-cqrs/compare/v0.16.4...v1.0.0-rc.0) (2024-08-02)
35
+
36
+
37
+ ### Fixes
38
+
39
+ * Vulnerability in minimist dependency ([07b8c68](https://github.com/snatalenko/node-cqrs/commit/07b8c682fae4278965aa13a06caa994c037934e9))
40
+
41
+ ### Refactoring
42
+
43
+ * Migrate to TS and Jest ([6737d55](https://github.com/snatalenko/node-cqrs/commit/6737d5566a9dc6314df0b20a65d32414fc503e54))
44
+
45
+
1
46
  ## [0.16.4](https://github.com/snatalenko/node-cqrs/compare/v0.16.3...v0.16.4) (2022-08-28)
2
47
 
3
48
 
package/README.md CHANGED
@@ -2,7 +2,8 @@ node-cqrs
2
2
  =========
3
3
 
4
4
  [![NPM Version](https://img.shields.io/npm/v/node-cqrs.svg)](https://www.npmjs.com/package/node-cqrs)
5
- [![Build Status](https://secure.travis-ci.org/snatalenko/node-cqrs.svg?branch=master)](http://travis-ci.org/snatalenko/node-cqrs)
5
+ [![Audit Status](https://github.com/snatalenko/node-cqrs/actions/workflows/audit.yml/badge.svg)](https://github.com/snatalenko/node-cqrs/actions/workflows/audit.yml)
6
+ [![Tests Status](https://github.com/snatalenko/node-cqrs/actions/workflows/tests.yml/badge.svg)](https://github.com/snatalenko/node-cqrs/actions/workflows/tests.yml)
6
7
  [![Coverage Status](https://coveralls.io/repos/github/snatalenko/node-cqrs/badge.svg?branch=master)](https://coveralls.io/github/snatalenko/node-cqrs?branch=master)
7
8
  [![NPM Downloads](https://img.shields.io/npm/dm/node-cqrs.svg)](https://www.npmjs.com/package/node-cqrs)
8
9
 
@@ -0,0 +1,178 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AbstractAggregate = void 0;
4
+ const utils_1 = require("./utils");
5
+ /**
6
+ * Deep-clone simple JS object
7
+ */
8
+ function clone(obj) {
9
+ return JSON.parse(JSON.stringify(obj));
10
+ }
11
+ const SNAPSHOT_EVENT_TYPE = 'snapshot';
12
+ /**
13
+ * Base class for Aggregate definition
14
+ */
15
+ class AbstractAggregate {
16
+ /**
17
+ * Optional list of commands handled by Aggregate.
18
+ *
19
+ * If not overridden in Aggregate implementation,
20
+ * `AggregateCommandHandler` will treat all public methods as command handlers
21
+ *
22
+ * @example
23
+ * return ['createUser', 'changePassword'];
24
+ */
25
+ static get handles() {
26
+ return undefined;
27
+ }
28
+ #id;
29
+ #changes = [];
30
+ #version = 0;
31
+ #snapshotVersion;
32
+ /** Internal aggregate state */
33
+ state;
34
+ /** Command being handled by aggregate */
35
+ command;
36
+ /** Unique aggregate instance identifier */
37
+ get id() {
38
+ return this.#id;
39
+ }
40
+ /** Aggregate instance version */
41
+ get version() {
42
+ return this.#version;
43
+ }
44
+ /** Restored snapshot version */
45
+ get snapshotVersion() {
46
+ return this.#snapshotVersion;
47
+ }
48
+ /** Events emitted by Aggregate */
49
+ get changes() {
50
+ return [...this.#changes];
51
+ }
52
+ /**
53
+ * Override to define whether an aggregate state snapshot should be taken
54
+ *
55
+ * @example
56
+ * // create snapshot every 50 events
57
+ * return this.version % 50 === 0;
58
+ */
59
+ get shouldTakeSnapshot() {
60
+ return false;
61
+ }
62
+ constructor(options) {
63
+ const { id, state, events } = options;
64
+ if (!id)
65
+ throw new TypeError('id argument required');
66
+ if (state && typeof state !== 'object')
67
+ throw new TypeError('state argument, when provided, must be an Object');
68
+ if (events && !Array.isArray(events))
69
+ throw new TypeError('events argument, when provided, must be an Array');
70
+ this.#id = id;
71
+ (0, utils_1.validateHandlers)(this);
72
+ if (state)
73
+ this.state = state;
74
+ if (events)
75
+ events.forEach(event => this.mutate(event));
76
+ }
77
+ /** Pass command to command handler */
78
+ handle(command) {
79
+ if (!command)
80
+ throw new TypeError('command argument required');
81
+ if (!command.type)
82
+ throw new TypeError('command.type argument required');
83
+ const handler = (0, utils_1.getHandler)(this, command.type);
84
+ if (!handler)
85
+ throw new Error(`'${command.type}' handler is not defined or not a function`);
86
+ this.command = command;
87
+ return handler.call(this, command.payload, command.context);
88
+ }
89
+ /** Mutate aggregate state and increment aggregate version */
90
+ mutate(event) {
91
+ if (event.aggregateVersion !== undefined)
92
+ this.#version = event.aggregateVersion;
93
+ if (event.type === SNAPSHOT_EVENT_TYPE) {
94
+ this.#snapshotVersion = event.aggregateVersion;
95
+ this.restoreSnapshot(event);
96
+ }
97
+ else if (this.state) {
98
+ const handler = 'mutate' in this.state ?
99
+ this.state.mutate :
100
+ (0, utils_1.getHandler)(this.state, event.type);
101
+ if (handler)
102
+ handler.call(this.state, event);
103
+ }
104
+ this.#version += 1;
105
+ }
106
+ /** Format and register aggregate event and mutate aggregate state */
107
+ emit(type, payload) {
108
+ if (typeof type !== 'string' || !type.length)
109
+ throw new TypeError('type argument must be a non-empty string');
110
+ const event = this.makeEvent(type, payload, this.command);
111
+ this.emitRaw(event);
112
+ }
113
+ /** Format event based on a current aggregate state and a command being executed */
114
+ makeEvent(type, payload, sourceCommand) {
115
+ const event = {
116
+ aggregateId: this.id,
117
+ aggregateVersion: this.version,
118
+ type,
119
+ payload
120
+ };
121
+ if (sourceCommand) {
122
+ // augment event with command context
123
+ const { context, sagaId, sagaVersion } = sourceCommand;
124
+ if (context !== undefined)
125
+ event.context = context;
126
+ if (sagaId !== undefined)
127
+ event.sagaId = sagaId;
128
+ if (sagaVersion !== undefined)
129
+ event.sagaVersion = sagaVersion;
130
+ }
131
+ return event;
132
+ }
133
+ /** Register aggregate event and mutate aggregate state */
134
+ emitRaw(event) {
135
+ if (!event)
136
+ throw new TypeError('event argument required');
137
+ if (!event.aggregateId)
138
+ throw new TypeError('event.aggregateId argument required');
139
+ if (typeof event.aggregateVersion !== 'number')
140
+ throw new TypeError('event.aggregateVersion argument must be a Number');
141
+ if (typeof event.type !== 'string' || !event.type.length)
142
+ throw new TypeError('event.type argument must be a non-empty String');
143
+ this.mutate(event);
144
+ this.#changes.push(event);
145
+ }
146
+ /**
147
+ * Take an aggregate state snapshot and add it to the changes queue
148
+ */
149
+ takeSnapshot() {
150
+ this.emit(SNAPSHOT_EVENT_TYPE, this.makeSnapshot());
151
+ }
152
+ /** Create an aggregate state snapshot */
153
+ makeSnapshot() {
154
+ if (!this.state)
155
+ throw new Error('state property is empty, either define state or override makeSnapshot method');
156
+ return clone(this.state);
157
+ }
158
+ /** Restore aggregate state from a snapshot */
159
+ restoreSnapshot(snapshotEvent) {
160
+ if (!snapshotEvent)
161
+ throw new TypeError('snapshotEvent argument required');
162
+ if (!snapshotEvent.type)
163
+ throw new TypeError('snapshotEvent.type argument required');
164
+ if (!snapshotEvent.payload)
165
+ throw new TypeError('snapshotEvent.payload argument required');
166
+ if (snapshotEvent.type !== SNAPSHOT_EVENT_TYPE)
167
+ throw new Error(`${SNAPSHOT_EVENT_TYPE} event type expected`);
168
+ if (!this.state)
169
+ throw new Error('state property is empty, either defined state or override restoreSnapshot method');
170
+ Object.assign(this.state, clone(snapshotEvent.payload));
171
+ }
172
+ /** Get human-readable aggregate identifier */
173
+ toString() {
174
+ return `${(0, utils_1.getClassName)(this)} ${this.id} (v${this.version})`;
175
+ }
176
+ }
177
+ exports.AbstractAggregate = AbstractAggregate;
178
+ //# sourceMappingURL=AbstractAggregate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AbstractAggregate.js","sourceRoot":"","sources":["../src/AbstractAggregate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AbstractProjection = void 0;
4
+ const InMemoryView_1 = require("./infrastructure/InMemoryView");
5
+ const utils_1 = require("./utils");
6
+ const isProjectionView = (view) => 'ready' in view &&
7
+ 'lock' in view &&
8
+ 'unlock' in view &&
9
+ 'once' in view;
10
+ const asProjectionView = (view) => (isProjectionView(view) ? view : undefined);
11
+ /**
12
+ * Base class for Projection definition
13
+ */
14
+ class AbstractProjection {
15
+ /**
16
+ * Optional list of event types being handled by projection.
17
+ * Can be overridden in projection implementation.
18
+ * If not overridden, will detect event types from event handlers declared on the Projection class
19
+ */
20
+ static get handles() {
21
+ return undefined;
22
+ }
23
+ /**
24
+ * Default view associated with projection
25
+ */
26
+ get view() {
27
+ if (!this.#view)
28
+ this.#view = this.#viewFactory();
29
+ return this.#view;
30
+ }
31
+ #viewFactory;
32
+ #view;
33
+ _logger;
34
+ get collectionName() {
35
+ return (0, utils_1.getClassName)(this);
36
+ }
37
+ /**
38
+ * Indicates if view should be restored from EventStore on start.
39
+ * Override for custom behavior.
40
+ */
41
+ get shouldRestoreView() {
42
+ return (this.view instanceof Map)
43
+ || (this.view instanceof InMemoryView_1.InMemoryView);
44
+ }
45
+ constructor({ view, viewFactory = InMemoryView_1.InMemoryView.factory, logger } = {}) {
46
+ (0, utils_1.validateHandlers)(this);
47
+ this.#viewFactory = view ?
48
+ () => view :
49
+ viewFactory;
50
+ this._logger = logger && 'child' in logger ?
51
+ logger.child({ service: (0, utils_1.getClassName)(this) }) :
52
+ logger;
53
+ }
54
+ /** Subscribe to event store */
55
+ async subscribe(eventStore) {
56
+ (0, utils_1.subscribe)(eventStore, this, {
57
+ masterHandler: (e) => this.project(e)
58
+ });
59
+ await this.restore(eventStore);
60
+ }
61
+ /** Pass event to projection event handler */
62
+ async project(event) {
63
+ const concurrentView = asProjectionView(this.view);
64
+ if (concurrentView && !concurrentView.ready)
65
+ await concurrentView.once('ready');
66
+ return this._project(event);
67
+ }
68
+ /** Pass event to projection event handler, without awaiting for restore operation to complete */
69
+ async _project(event) {
70
+ const handler = (0, utils_1.getHandler)(this, event.type);
71
+ if (!handler)
72
+ throw new Error(`'${event.type}' handler is not defined or not a function`);
73
+ return handler.call(this, event);
74
+ }
75
+ /** Restore projection view from event store */
76
+ async restore(eventStore) {
77
+ // lock the view to ensure same restoring procedure
78
+ // won't be performed by another projection instance
79
+ const concurrentView = asProjectionView(this.view);
80
+ if (concurrentView)
81
+ await concurrentView.lock();
82
+ const shouldRestore = await this.shouldRestoreView;
83
+ if (shouldRestore)
84
+ await this._restore(eventStore);
85
+ if (concurrentView)
86
+ concurrentView.unlock();
87
+ }
88
+ /** Restore projection view from event store */
89
+ async _restore(eventStore) {
90
+ if (!eventStore)
91
+ throw new TypeError('eventStore argument required');
92
+ if (typeof eventStore.getAllEvents !== 'function')
93
+ throw new TypeError('eventStore.getAllEvents must be a Function');
94
+ this._logger?.debug('retrieving events and restoring projection...');
95
+ const messageTypes = (0, utils_1.getHandledMessageTypes)(this);
96
+ const eventsIterable = eventStore.getAllEvents(messageTypes);
97
+ let eventsCount = 0;
98
+ const startTs = Date.now();
99
+ for await (const event of eventsIterable) {
100
+ try {
101
+ await this._project(event);
102
+ eventsCount += 1;
103
+ }
104
+ catch (err) {
105
+ this._onRestoringError(err, event);
106
+ }
107
+ }
108
+ this._logger?.info(`view restored (${this.view}) from ${eventsCount} event(s) in ${Date.now() - startTs} ms`);
109
+ }
110
+ /** Handle error on restoring. Logs and throws error by default */
111
+ _onRestoringError(error, event) {
112
+ this._logger?.error(`view restoring has failed (view will remain locked): ${error.message}`, {
113
+ service: (0, utils_1.getClassName)(this),
114
+ event,
115
+ stack: error.stack
116
+ });
117
+ throw error;
118
+ }
119
+ }
120
+ exports.AbstractProjection = AbstractProjection;
121
+ //# sourceMappingURL=AbstractProjection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AbstractProjection.js","sourceRoot":"","sources":["../src/AbstractProjection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AbstractSaga = void 0;
4
+ const utils_1 = require("./utils");
5
+ /**
6
+ * Base class for Saga definition
7
+ */
8
+ class AbstractSaga {
9
+ /** List of events that start new saga, must be overridden in Saga implementation */
10
+ static get startsWith() {
11
+ throw new Error('startsWith must be overridden to return a list of event types that start saga');
12
+ }
13
+ /** List of event types being handled by Saga, must be overridden in Saga implementation */
14
+ static get handles() {
15
+ return [];
16
+ }
17
+ /** Saga ID */
18
+ get id() {
19
+ return this.#id;
20
+ }
21
+ /** Saga version */
22
+ get version() {
23
+ return this.#version;
24
+ }
25
+ /** Command execution queue */
26
+ get uncommittedMessages() {
27
+ return Array.from(this.#messages);
28
+ }
29
+ #id;
30
+ #version = 0;
31
+ #messages = [];
32
+ /**
33
+ * Creates an instance of AbstractSaga
34
+ */
35
+ constructor(options) {
36
+ if (!options)
37
+ throw new TypeError('options argument required');
38
+ if (!options.id)
39
+ throw new TypeError('options.id argument required');
40
+ this.#id = options.id;
41
+ (0, utils_1.validateHandlers)(this, 'startsWith');
42
+ (0, utils_1.validateHandlers)(this, 'handles');
43
+ if (options.events) {
44
+ options.events.forEach(e => this.apply(e));
45
+ this.resetUncommittedMessages();
46
+ }
47
+ Object.defineProperty(this, 'restored', { value: true });
48
+ }
49
+ /** Modify saga state by applying an event */
50
+ apply(event) {
51
+ if (!event)
52
+ throw new TypeError('event argument required');
53
+ if (!event.type)
54
+ throw new TypeError('event.type argument required');
55
+ const handler = (0, utils_1.getHandler)(this, event.type);
56
+ if (!handler)
57
+ throw new Error(`'${event.type}' handler is not defined or not a function`);
58
+ const r = handler.call(this, event);
59
+ if (r instanceof Promise) {
60
+ return r.then(() => {
61
+ this.#version += 1;
62
+ });
63
+ }
64
+ this.#version += 1;
65
+ return undefined;
66
+ }
67
+ /** Format a command and put it to the execution queue */
68
+ enqueue(commandType, aggregateId, payload) {
69
+ if (typeof commandType !== 'string' || !commandType.length)
70
+ throw new TypeError('commandType argument must be a non-empty String');
71
+ if (!['string', 'number', 'undefined'].includes(typeof aggregateId))
72
+ throw new TypeError('aggregateId argument must be either string, number or undefined');
73
+ this.enqueueRaw({
74
+ aggregateId,
75
+ sagaId: this.id,
76
+ sagaVersion: this.version,
77
+ type: commandType,
78
+ payload
79
+ });
80
+ }
81
+ /** Put a command to the execution queue */
82
+ enqueueRaw(command) {
83
+ if (typeof command !== 'object' || !command)
84
+ throw new TypeError('command argument must be an Object');
85
+ if (typeof command.type !== 'string' || !command.type.length)
86
+ throw new TypeError('command.type argument must be a non-empty String');
87
+ this.#messages.push(command);
88
+ }
89
+ /** Clear the execution queue */
90
+ resetUncommittedMessages() {
91
+ this.#messages.length = 0;
92
+ }
93
+ /** Get human-readable Saga name */
94
+ toString() {
95
+ return `${(0, utils_1.getClassName)(this)} ${this.id} (v${this.version})`;
96
+ }
97
+ }
98
+ exports.AbstractSaga = AbstractSaga;
99
+ //# sourceMappingURL=AbstractSaga.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AbstractSaga.js","sourceRoot":"","sources":["../src/AbstractSaga.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AggregateCommandHandler = void 0;
4
+ const utils_1 = require("./utils");
5
+ /**
6
+ * Aggregate command handler.
7
+ *
8
+ * Subscribes to event store and awaits aggregate commands.
9
+ * Upon command receiving creates an instance of aggregate,
10
+ * restores its state, passes command and commits emitted events to event store.
11
+ */
12
+ class AggregateCommandHandler {
13
+ #eventStore;
14
+ #logger;
15
+ #aggregateFactory;
16
+ #handles;
17
+ constructor({ eventStore, aggregateType, aggregateFactory, handles, logger }) {
18
+ if (!eventStore)
19
+ throw new TypeError('eventStore argument required');
20
+ this.#eventStore = eventStore;
21
+ this.#logger = logger && 'child' in logger ?
22
+ logger.child({ service: (0, utils_1.getClassName)(this) }) :
23
+ logger;
24
+ if (aggregateType) {
25
+ const AggregateType = aggregateType;
26
+ this.#aggregateFactory = params => new AggregateType(params);
27
+ this.#handles = (0, utils_1.getHandledMessageTypes)(AggregateType);
28
+ }
29
+ else if (aggregateFactory) {
30
+ if (!Array.isArray(handles) || !handles.length)
31
+ throw new TypeError('handles argument must be an non-empty Array');
32
+ this.#aggregateFactory = aggregateFactory;
33
+ this.#handles = handles;
34
+ }
35
+ else {
36
+ throw new TypeError('either aggregateType or aggregateFactory is required');
37
+ }
38
+ }
39
+ /** Subscribe to all command types handled by aggregateType */
40
+ subscribe(commandBus) {
41
+ (0, utils_1.subscribe)(commandBus, this, {
42
+ messageTypes: this.#handles,
43
+ masterHandler: (c) => this.execute(c)
44
+ });
45
+ }
46
+ /** Restore aggregate from event store events */
47
+ async #restoreAggregate(id) {
48
+ if (!id)
49
+ throw new TypeError('id argument required');
50
+ const events = await this.#eventStore.getAggregateEvents(id);
51
+ const aggregate = this.#aggregateFactory({ id, events });
52
+ this.#logger?.info(`${aggregate} state restored from ${events.length} event(s)`);
53
+ return aggregate;
54
+ }
55
+ /** Create new aggregate with new Id generated by event store */
56
+ async #createAggregate() {
57
+ const id = await this.#eventStore.getNewId();
58
+ const aggregate = this.#aggregateFactory({ id });
59
+ this.#logger?.info(`${aggregate} created`);
60
+ return aggregate;
61
+ }
62
+ /** Pass a command to corresponding aggregate */
63
+ async execute(cmd) {
64
+ if (!cmd)
65
+ throw new TypeError('cmd argument required');
66
+ if (!cmd.type)
67
+ throw new TypeError('cmd.type argument required');
68
+ const aggregate = cmd.aggregateId ?
69
+ await this.#restoreAggregate(cmd.aggregateId) :
70
+ await this.#createAggregate();
71
+ await aggregate.handle(cmd);
72
+ let events = aggregate.changes;
73
+ this.#logger?.info(`${aggregate} "${cmd.type}" command processed, ${events.length} event(s) produced`);
74
+ if (!events.length)
75
+ return events;
76
+ if (aggregate.shouldTakeSnapshot && this.#eventStore.snapshotsSupported) {
77
+ aggregate.takeSnapshot();
78
+ events = aggregate.changes;
79
+ }
80
+ await this.#eventStore.commit(events);
81
+ return events;
82
+ }
83
+ }
84
+ exports.AggregateCommandHandler = AggregateCommandHandler;
85
+ //# sourceMappingURL=AggregateCommandHandler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AggregateCommandHandler.js","sourceRoot":"","sources":["../src/AggregateCommandHandler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CommandBus = void 0;
4
+ const InMemoryMessageBus_1 = require("./infrastructure/InMemoryMessageBus");
5
+ class CommandBus {
6
+ #logger;
7
+ #bus;
8
+ /**
9
+ * Creates an instance of CommandBus.
10
+ */
11
+ constructor({ messageBus, logger }) {
12
+ this.#bus = messageBus ?? new InMemoryMessageBus_1.InMemoryMessageBus();
13
+ this.#logger = logger && 'child' in logger ?
14
+ logger.child({ service: 'CommandBus' }) :
15
+ logger;
16
+ }
17
+ /**
18
+ * Set up a command handler
19
+ */
20
+ on(commandType, handler) {
21
+ if (typeof commandType !== 'string' || !commandType.length)
22
+ throw new TypeError('commandType argument must be a non-empty String');
23
+ if (typeof handler !== 'function')
24
+ throw new TypeError('handler argument must be a Function');
25
+ return this.#bus.on(commandType, handler);
26
+ }
27
+ /**
28
+ * Remove previously installed command handler
29
+ */
30
+ off(commandType, handler) {
31
+ if (typeof commandType !== 'string' || !commandType.length)
32
+ throw new TypeError('commandType argument must be a non-empty String');
33
+ if (typeof handler !== 'function')
34
+ throw new TypeError('handler argument must be a Function');
35
+ return this.#bus.off(commandType, handler);
36
+ }
37
+ /**
38
+ * Format and send a command for execution
39
+ */
40
+ send(type, aggregateId, options, ...otherArgs) {
41
+ if (typeof type !== 'string' || !type.length)
42
+ throw new TypeError('type argument must be a non-empty String');
43
+ if (options && typeof options !== 'object')
44
+ throw new TypeError('options argument, when defined, must be an Object');
45
+ if (otherArgs.length > 1)
46
+ throw new TypeError('more than expected arguments supplied');
47
+ // obsolete. left for backward compatibility
48
+ const optionsContainContext = options && !('context' in options) && !('payload' in options);
49
+ if (otherArgs.length || optionsContainContext) {
50
+ const context = options;
51
+ const payload = otherArgs.length ? otherArgs[0] : undefined;
52
+ return this.sendRaw({ type, aggregateId, context, payload });
53
+ }
54
+ return this.sendRaw({ type, aggregateId, ...options });
55
+ }
56
+ /**
57
+ * Send a command for execution
58
+ */
59
+ sendRaw(command) {
60
+ if (!command)
61
+ throw new TypeError('command argument required');
62
+ if (!command.type)
63
+ throw new TypeError('command.type argument required');
64
+ this.#logger?.debug(`sending '${command.type}' command${command.aggregateId ? ` to ${command.aggregateId}` : ''}...`);
65
+ return this.#bus.send(command).then(r => {
66
+ this.#logger?.debug(`'${command.type}' ${command.aggregateId ? `on ${command.aggregateId}` : ''} processed`);
67
+ return r;
68
+ }, error => {
69
+ this.#logger?.warn(`'${command.type}' ${command.aggregateId ? `on ${command.aggregateId}` : ''} processing has failed: ${error.message}`, {
70
+ stack: error.stack
71
+ });
72
+ throw error;
73
+ });
74
+ }
75
+ }
76
+ exports.CommandBus = CommandBus;
77
+ //# sourceMappingURL=CommandBus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommandBus.js","sourceRoot":"","sources":["../src/CommandBus.ts"],"names":[],"mappings":"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"}