node-cqrs 0.16.3 → 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 (135) hide show
  1. package/CHANGELOG.md +53 -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 +26 -17
  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/index.d.ts +0 -43
  89. package/index.js +0 -3
  90. package/jsconfig.json +0 -15
  91. package/src/AbstractAggregate.js +0 -277
  92. package/src/AbstractProjection.js +0 -192
  93. package/src/AbstractSaga.js +0 -171
  94. package/src/AggregateCommandHandler.js +0 -126
  95. package/src/CommandBus.js +0 -91
  96. package/src/CqrsContainerBuilder.js +0 -134
  97. package/src/EventStore.js +0 -457
  98. package/src/EventStream.js +0 -63
  99. package/src/SagaEventHandler.js +0 -141
  100. package/src/index.js +0 -21
  101. package/src/infrastructure/InMemoryEventStorage.js +0 -76
  102. package/src/infrastructure/InMemoryMessageBus.js +0 -132
  103. package/src/infrastructure/InMemorySnapshotStorage.js +0 -40
  104. package/src/infrastructure/InMemoryView.js +0 -265
  105. package/src/utils/getClassName.js +0 -11
  106. package/src/utils/index.js +0 -6
  107. package/src/utils/nullLogger.js +0 -8
  108. package/types/classes/AbstractAggregate.d.ts +0 -64
  109. package/types/classes/AbstractProjection.d.ts +0 -46
  110. package/types/classes/AbstractSaga.d.ts +0 -39
  111. package/types/classes/AggregateCommandHandler.d.ts +0 -21
  112. package/types/classes/CommandBus.d.ts +0 -17
  113. package/types/classes/CqrsContainerBuilder.d.ts +0 -26
  114. package/types/classes/EventStore.d.ts +0 -53
  115. package/types/classes/EventStream.d.ts +0 -18
  116. package/types/classes/InMemoryEventStorage.d.ts +0 -21
  117. package/types/classes/InMemoryMessageBus.d.ts +0 -30
  118. package/types/classes/InMemorySnapshotStorage.d.ts +0 -18
  119. package/types/classes/InMemoryView.d.ts +0 -57
  120. package/types/classes/SagaEventHandler.d.ts +0 -20
  121. package/types/interfaces/IAggregate.d.ts +0 -30
  122. package/types/interfaces/IAggregateSnapshotStorage.d.ts +0 -4
  123. package/types/interfaces/ICommandBus.d.ts +0 -6
  124. package/types/interfaces/ICommandHandler.d.ts +0 -3
  125. package/types/interfaces/IConcurrentView.d.ts +0 -22
  126. package/types/interfaces/IEventReceptor.d.ts +0 -3
  127. package/types/interfaces/IEventStorage.d.ts +0 -20
  128. package/types/interfaces/IEventStore.d.ts +0 -19
  129. package/types/interfaces/IEventStream.d.ts +0 -13
  130. package/types/interfaces/ILogger.d.ts +0 -3
  131. package/types/interfaces/IMessageBus.d.ts +0 -5
  132. package/types/interfaces/IObserver.d.ts +0 -11
  133. package/types/interfaces/IProjection.d.ts +0 -10
  134. package/types/interfaces/ISaga.d.ts +0 -27
  135. package/types/interfaces/Identifier.d.ts +0 -1
@@ -0,0 +1,328 @@
1
+ export type Identifier = string | number;
2
+
3
+ export interface IMessage<TPayload = any> {
4
+ /** Event or command type */
5
+ type: string;
6
+
7
+ aggregateId?: Identifier;
8
+ aggregateVersion?: number;
9
+
10
+ sagaId?: Identifier;
11
+ sagaVersion?: number;
12
+
13
+ payload?: TPayload;
14
+ context?: any;
15
+ }
16
+
17
+ export type ICommand<TPayload = any> = IMessage<TPayload>;
18
+
19
+ export type IEvent<TPayload = any> = IMessage<TPayload> & {
20
+ /** Unique event identifier */
21
+ id?: string;
22
+ };
23
+
24
+ /**
25
+ * @deprecated Try to use `IEventStream` instead
26
+ */
27
+ export type IEventSet = ReadonlyArray<Readonly<IEvent>>;
28
+
29
+ export type IEventStream = AsyncIterableIterator<Readonly<IEvent>>;
30
+
31
+
32
+ /**
33
+ * Minimum aggregate interface, as it's used by default `AggregateCommandHandler`
34
+ */
35
+ export interface IAggregate {
36
+
37
+ /** Unique aggregate identifier */
38
+ readonly id: Identifier;
39
+
40
+ /** Main entry point for aggregate commands */
41
+ handle(command: ICommand): void | Promise<void>;
42
+
43
+ /** List of events emitted by Aggregate as a result of handling command(s) */
44
+ readonly changes: IEventSet;
45
+
46
+ /** An indicator if aggregate snapshot should be taken */
47
+ readonly shouldTakeSnapshot?: boolean;
48
+
49
+ /** Take an aggregate state snapshot and add it to the changes queue */
50
+ takeSnapshot(): void;
51
+ }
52
+
53
+ export interface IMutableAggregateState {
54
+ // schemaVersion?: number;
55
+ // constructor: IAggregateStateConstructor;
56
+ mutate(event: IEvent): void;
57
+ }
58
+
59
+ // export interface IAggregateStateConstructor extends Function {
60
+ // schemaVersion?: number;
61
+ // new(): IAggregateState;
62
+ // }
63
+
64
+ export type IAggregateConstructorParams<TState extends IMutableAggregateState | object | void> = {
65
+ /** Unique aggregate identifier */
66
+ id: Identifier,
67
+
68
+ /** Aggregate events, logged after latest snapshot */
69
+ events?: IEventSet,
70
+
71
+ /** Aggregate state instance */
72
+ state?: TState
73
+ };
74
+
75
+ export interface IAggregateConstructor<TState extends IMutableAggregateState | object | void> {
76
+ readonly handles?: string[];
77
+ new(options: IAggregateConstructorParams<TState>): IAggregate;
78
+ }
79
+
80
+ export type IAggregateFactory<TState extends IMutableAggregateState | object | void> =
81
+ (options: IAggregateConstructorParams<TState>) => IAggregate;
82
+
83
+ export interface ISaga {
84
+ /** Unique Saga ID */
85
+ readonly id: Identifier;
86
+
87
+ /** List of commands emitted by Saga */
88
+ readonly uncommittedMessages: ICommand[];
89
+
90
+ /** Main entry point for Saga events */
91
+ apply(event: IEvent): void | Promise<void>;
92
+
93
+ /** Reset emitted commands when they are not longer needed */
94
+ resetUncommittedMessages(): void;
95
+
96
+ onError?(error: Error, options: { event: IEvent, command: ICommand }): void;
97
+ }
98
+
99
+ export type ISagaConstructorParams = {
100
+ id: Identifier,
101
+ events?: IEventSet
102
+ };
103
+
104
+ export type ISagaFactory = (options: ISagaConstructorParams) => ISaga;
105
+
106
+ export interface ISagaConstructor {
107
+ new(options: ISagaConstructorParams): ISaga;
108
+
109
+ /** List of event types that trigger new saga start */
110
+ readonly startsWith: string[];
111
+
112
+ /** List of events being handled by Saga */
113
+ readonly handles: string[];
114
+ }
115
+
116
+ export interface IMessageHandler {
117
+ (...args: any[]): any | Promise<any>
118
+ };
119
+
120
+ export interface IObservable {
121
+ on(type: string, handler: IMessageHandler): void;
122
+
123
+ off(type: string, handler: IMessageHandler): void;
124
+
125
+ queue?(name: string): IObservable;
126
+ }
127
+
128
+ export interface IObserver {
129
+ subscribe(observable: IObservable): void;
130
+ }
131
+
132
+ /** Commands */
133
+
134
+ export interface ICommandBus extends IObservable {
135
+ send(commandType: string, aggregateId: Identifier, options: { payload?: object, context?: object }):
136
+ Promise<IEventSet>;
137
+
138
+ sendRaw(command: ICommand):
139
+ Promise<IEventSet>;
140
+
141
+ on(type: string, handler: IMessageHandler): void;
142
+ }
143
+
144
+ export interface ICommandHandler extends IObserver {
145
+ subscribe(commandBus: ICommandBus): void;
146
+ }
147
+
148
+ /** Events */
149
+
150
+ export type IEventQueryFilter = {
151
+ /** Get events emitted after this specific event */
152
+ afterEvent?: IEvent;
153
+
154
+ /** Get events emitted before this specific event */
155
+ beforeEvent?: IEvent;
156
+ }
157
+
158
+ export interface IEventStorage {
159
+ /**
160
+ * Create unique identifier
161
+ */
162
+ getNewId(): Identifier | Promise<Identifier>;
163
+
164
+ commitEvents(events: IEventSet): Promise<IEventSet>;
165
+
166
+ getEvents(eventTypes?: Readonly<string[]>): IEventStream;
167
+
168
+ getAggregateEvents(aggregateId: Identifier, options?: { snapshot?: IEvent }): Promise<IEventSet>;
169
+
170
+ getSagaEvents(sagaId: Identifier, options: Pick<IEventQueryFilter, "beforeEvent">): Promise<IEventSet>;
171
+ }
172
+
173
+ export interface IEventStore extends IObservable {
174
+ readonly snapshotsSupported?: boolean;
175
+
176
+ getNewId(): Identifier | Promise<Identifier>;
177
+
178
+ commit(events: IEventSet): Promise<IEventSet>;
179
+
180
+ getAllEvents(eventTypes?: Readonly<string[]>): IEventStream;
181
+
182
+ getAggregateEvents(aggregateId: Identifier, options?: { snapshot?: IEvent }): Promise<IEventSet>;
183
+
184
+ getSagaEvents(sagaId: Identifier, options: Pick<IEventQueryFilter, "beforeEvent">): Promise<IEventSet>;
185
+
186
+ once(messageTypes: string | string[], handler?: IMessageHandler, filter?: (e: IEvent) => boolean): Promise<IEvent>;
187
+
188
+ queue(name: string): IObservable;
189
+
190
+ registerSagaStarters(startsWith: string[] | undefined): void;
191
+ }
192
+
193
+ export interface IEventReceptor extends IObserver {
194
+ subscribe(eventStore: IEventStore): void;
195
+ }
196
+
197
+ export interface IMessageBus extends IObservable {
198
+ send(command: ICommand): Promise<any>;
199
+ publish(event: IEvent): Promise<any>;
200
+ }
201
+
202
+
203
+ /** Projection */
204
+
205
+ export interface IProjection<TView extends object> extends IObserver {
206
+ readonly view: TView;
207
+
208
+ subscribe(eventStore: IEventStore): Promise<void>;
209
+
210
+ project(event: IEvent): Promise<void>;
211
+ }
212
+
213
+ export interface IProjectionConstructor {
214
+ new(c?: any): IProjection<any>;
215
+ readonly handles?: string[];
216
+ }
217
+
218
+ // export type ProjectionViewFactoryParams = {
219
+ // schemaVersion: string,
220
+ // collectionName: string
221
+ // }
222
+
223
+ export interface IViewFactory<TView> {
224
+ (): TView;
225
+ }
226
+
227
+ export interface ILockable {
228
+ lock(): Promise<any>;
229
+ unlock(): Promise<any>;
230
+ }
231
+
232
+ export interface ILockableWithIndication extends ILockable {
233
+ locked: Readonly<boolean>;
234
+ once(event: 'unlocked'): Promise<void>;
235
+ }
236
+
237
+ export interface IProjectionView extends ILockable {
238
+
239
+ /**
240
+ * Indicates if view is ready for new events projecting
241
+ */
242
+ ready: boolean;
243
+
244
+ /**
245
+ * Lock the view for external reads/writes
246
+ */
247
+ lock(): Promise<boolean>;
248
+
249
+ /**
250
+ * Unlock external read/write operations
251
+ */
252
+ unlock(): Promise<void>;
253
+
254
+ /**
255
+ * Wait till the view is ready to accept new events
256
+ */
257
+ once(eventType: "ready"): Promise<void>;
258
+ }
259
+
260
+ export interface IPersistentView extends IProjectionView {
261
+
262
+ /**
263
+ * Get last projected event
264
+ */
265
+ getLastEvent(): Promise<IEvent | undefined>;
266
+
267
+ /**
268
+ * Mark event as projecting to prevent its handling by another
269
+ * projection instance working with the same storage.
270
+ *
271
+ * @returns False value if event is already processing or processed
272
+ */
273
+ tryMarkAsProjecting(event: IEvent<any>): Promise<boolean>;
274
+
275
+ /**
276
+ * Mark event as projected
277
+ */
278
+ markAsProjected(event: IEvent<any>): Promise<void>;
279
+ }
280
+
281
+
282
+ /** Snapshots */
283
+
284
+ type TSnapshot<TPayload = object> = {
285
+ /**
286
+ * Schema version of the data stored in `state` property.
287
+ * Snapshots with older schema versions must be passed thru a data migration before applying for a newer schema
288
+ */
289
+ schemaVersion: string | number;
290
+
291
+ /**
292
+ * Last event that was processed before making a snapshot
293
+ */
294
+ lastEvent: IEvent;
295
+
296
+ /**
297
+ * Snapshot data
298
+ */
299
+ data: TPayload;
300
+ }
301
+
302
+ interface ISnapshotStorage {
303
+ getSnapshot(id: Identifier): Promise<TSnapshot>;
304
+ saveSnapshot(id: Identifier, snapshot: TSnapshot): Promise<void>;
305
+ }
306
+
307
+ type ISnapshotEvent<TPayload> = IEvent<TSnapshot<TPayload>>;
308
+
309
+ export interface IAggregateSnapshotStorage {
310
+ getAggregateSnapshot<TState>(aggregateId: Identifier): Promise<IEvent<TState> | undefined> | IEvent<TState> | undefined;
311
+
312
+ saveAggregateSnapshot<TState>(snapshotEvent: IEvent<TState>): Promise<void> | void;
313
+ }
314
+
315
+
316
+ /** Interfaces */
317
+
318
+ export interface ILogger {
319
+ log(level: 'debug' | 'info' | 'warn' | 'error', message: string, meta?: { [key: string]: any }): void;
320
+ debug(message: string, meta?: { [key: string]: any }): void;
321
+ info(message: string, meta?: { [key: string]: any }): void;
322
+ warn(message: string, meta?: { [key: string]: any }): void;
323
+ error(message: string, meta?: { [key: string]: any }): void;
324
+ }
325
+
326
+ export interface IExtendableLogger extends ILogger {
327
+ child(meta?: { [key: string]: any }): IExtendableLogger;
328
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Get instance class name
3
+ */
4
+ export function getClassName(instance: object): string {
5
+ return Object.getPrototypeOf(instance).constructor.name;
6
+ }
@@ -1,13 +1,11 @@
1
- 'use strict';
2
-
3
- const getMessageHandlerNames = require('./getMessageHandlerNames');
1
+ import { getMessageHandlerNames } from './getMessageHandlerNames';
4
2
 
5
3
  /**
6
4
  * Get a list of message types handled by observer
7
- * @param {object | function} observerInstanceOrClass
8
- * @returns {string[]}
9
5
  */
10
- function getHandledMessageTypes(observerInstanceOrClass) {
6
+ export function getHandledMessageTypes(
7
+ observerInstanceOrClass: (object | Function) & { handles?: string[] }
8
+ ): string[] {
11
9
  if (!observerInstanceOrClass)
12
10
  throw new TypeError('observerInstanceOrClass argument required');
13
11
 
@@ -20,5 +18,3 @@ function getHandledMessageTypes(observerInstanceOrClass) {
20
18
 
21
19
  return getMessageHandlerNames(observerInstanceOrClass);
22
20
  }
23
-
24
- module.exports = getHandledMessageTypes;
@@ -1,14 +1,13 @@
1
- 'use strict';
1
+ import { IMessageHandler } from "../interfaces";
2
2
 
3
3
  /**
4
4
  * Gets a handler for a specific message type, prefers a public (w\o _ prefix) method, if available
5
- * @param {Object} context
6
- * @param {String} messageType
7
- * @return {IMessageHandler}
8
5
  */
9
- module.exports = function getHandler(context, messageType) {
10
- if (!context || typeof context !== 'object') throw new TypeError('context argument required');
11
- if (typeof messageType !== 'string' || !messageType.length) throw new TypeError('messageType argument must be a non-empty string');
6
+ export function getHandler(context: { [key: string]: any }, messageType: string): IMessageHandler | null {
7
+ if (!context || typeof context !== 'object')
8
+ throw new TypeError('context argument required');
9
+ if (typeof messageType !== 'string' || !messageType.length)
10
+ throw new TypeError('messageType argument must be a non-empty string');
12
11
 
13
12
  if (messageType in context && typeof context[messageType] === 'function')
14
13
  return context[messageType].bind(context);
@@ -1,10 +1,8 @@
1
- 'use strict';
2
-
3
1
  const KNOWN_METHOD_NAMES = new Set([
4
2
  'subscribe'
5
3
  ]);
6
4
 
7
- function getInheritedPropertyNames(prototype) {
5
+ function getInheritedPropertyNames(prototype: object): string[] {
8
6
  const parentPrototype = prototype && Object.getPrototypeOf(prototype);
9
7
  if (!parentPrototype)
10
8
  return [];
@@ -21,11 +19,8 @@ function getInheritedPropertyNames(prototype) {
21
19
  /**
22
20
  * Get message handler names from a command/event handler class.
23
21
  * Assumes all private method names start from underscore ("_").
24
- *
25
- * @param {any} observerInstanceOrClass Command or event handler class
26
- * @returns {string[]}
27
22
  */
28
- function getMessageHandlerNames(observerInstanceOrClass) {
23
+ export function getMessageHandlerNames(observerInstanceOrClass: (object | Function)): string[] {
29
24
  if (!observerInstanceOrClass)
30
25
  throw new TypeError('observerInstanceOrClass argument required');
31
26
 
@@ -47,5 +42,3 @@ function getMessageHandlerNames(observerInstanceOrClass) {
47
42
  !KNOWN_METHOD_NAMES.has(key) &&
48
43
  typeof propDescriptors[key].value === 'function');
49
44
  }
50
-
51
- module.exports = getMessageHandlerNames;
@@ -0,0 +1,8 @@
1
+ export * from './getClassName';
2
+ export * from './getHandler';
3
+ export * from './validateHandlers';
4
+ export * from './getMessageHandlerNames';
5
+ export * from './getHandledMessageTypes';
6
+ export * from './setupOneTimeEmitterSubscription';
7
+ export * from './subscribe';
8
+ export * from './isClass';
@@ -1,6 +1,4 @@
1
- 'use strict';
2
-
3
- module.exports = function isClass(func) {
1
+ export function isClass(func: Function) {
4
2
  return typeof func === 'function'
5
3
  && Function.prototype.toString.call(func).startsWith('class');
6
- };
4
+ }
@@ -0,0 +1,57 @@
1
+ import { IEvent, ILogger, IObservable } from "../interfaces";
2
+
3
+ /**
4
+ * Create one-time eventEmitter subscription for one or multiple events that match a filter
5
+ *
6
+ * @param {IObservable} emitter
7
+ * @param {string[]} messageTypes Array of event type to subscribe to
8
+ * @param {function(IEvent):any} [handler] Optional handler to execute for a first event received
9
+ * @param {function(IEvent):boolean} [filter] Optional filter to apply before executing a handler
10
+ * @param {ILogger} logger
11
+ * @return {Promise<IEvent>} Resolves to first event that passes filter
12
+ */
13
+ export function setupOneTimeEmitterSubscription(
14
+ emitter: IObservable,
15
+ messageTypes: string[],
16
+ filter?: (e: IEvent) => boolean,
17
+ handler?: (e: IEvent) => void,
18
+ logger?: ILogger
19
+ ): Promise<IEvent> {
20
+ if (typeof emitter !== 'object' || !emitter)
21
+ throw new TypeError('emitter argument must be an Object');
22
+ if (!Array.isArray(messageTypes) || messageTypes.some(m => !m || typeof m !== 'string'))
23
+ throw new TypeError('messageTypes argument must be an Array of non-empty Strings');
24
+ if (handler && typeof handler !== 'function')
25
+ throw new TypeError('handler argument, when specified, must be a Function');
26
+ if (filter && typeof filter !== 'function')
27
+ throw new TypeError('filter argument, when specified, must be a Function');
28
+
29
+ return new Promise(resolve => {
30
+
31
+ // handler will be invoked only once,
32
+ // even if multiple events have been emitted before subscription was destroyed
33
+ // https://nodejs.org/api/events.html#events_emitter_removelistener_eventname_listener
34
+ let handled = false;
35
+
36
+ function filteredHandler(event: IEvent) {
37
+ if (filter && !filter(event)) return;
38
+ if (handled) return;
39
+ handled = true;
40
+
41
+ for (const messageType of messageTypes)
42
+ emitter.off(messageType, filteredHandler);
43
+
44
+ logger?.debug(`'${event.type}' received, one-time subscription to '${messageTypes.join(',')}' removed`);
45
+
46
+ if (handler)
47
+ handler(event);
48
+
49
+ resolve(event);
50
+ }
51
+
52
+ for (const messageType of messageTypes)
53
+ emitter.on(messageType, filteredHandler);
54
+
55
+ logger?.debug(`set up one-time ${filter ? 'filtered subscription' : 'subscription'} to '${messageTypes.join(',')}'`);
56
+ });
57
+ }
@@ -1,21 +1,21 @@
1
- 'use strict';
1
+ import { IMessageHandler, IObservable } from "../interfaces";
2
+ import { getHandler } from './getHandler';
3
+ import { getHandledMessageTypes } from './getHandledMessageTypes';
2
4
 
3
- const { getHandler } = require('./utils');
4
- const getHandledMessageTypes = require('./utils/getHandledMessageTypes');
5
-
6
- const unique = arr => [...new Set(arr)];
5
+ const unique = <T>(arr: T[]): T[] => [...new Set(arr)];
7
6
 
8
7
  /**
9
8
  * Subscribe observer to observable
10
- *
11
- * @param {IObservable} observable
12
- * @param {object} observer
13
- * @param {object} [options]
14
- * @param {string[]} [options.messageTypes]
15
- * @param {IMessageHandler} [options.masterHandler]
16
- * @param {string} [options.queueName]
17
9
  */
18
- function subscribe(observable, observer, options = {}) {
10
+ export function subscribe(
11
+ observable: IObservable,
12
+ observer: object,
13
+ options: {
14
+ messageTypes?: string[],
15
+ masterHandler?: IMessageHandler,
16
+ queueName?: string
17
+ } = {}
18
+ ) {
19
19
  if (typeof observable !== 'object' || !observable)
20
20
  throw new TypeError('observable argument must be an Object');
21
21
  if (typeof observable.on !== 'function')
@@ -35,14 +35,17 @@ function subscribe(observable, observer, options = {}) {
35
35
 
36
36
  for (const messageType of unique(subscribeTo)) {
37
37
  const handler = masterHandler || getHandler(observer, messageType);
38
- if (!handler)
38
+ if (!handler)
39
39
  throw new Error(`'${messageType}' handler is not defined or not a function`);
40
40
 
41
- if (queueName)
41
+ if (queueName) {
42
+ if(!observable.queue)
43
+ throw new TypeError('Observer does not support named queues');
44
+
42
45
  observable.queue(queueName).on(messageType, handler);
43
- else
46
+ }
47
+ else {
44
48
  observable.on(messageType, handler);
49
+ }
45
50
  }
46
51
  }
47
-
48
- module.exports = subscribe;
@@ -1,13 +1,9 @@
1
- 'use strict';
2
-
3
- const getHandler = require('./getHandler');
1
+ import { getHandler } from './getHandler';
4
2
 
5
3
  /**
6
4
  * Ensure instance has handlers declared for all handled message types
7
- *
8
- * @param {object} instance
9
5
  */
10
- function validateHandlers(instance, handlesFieldName = 'handles') {
6
+ export function validateHandlers(instance: object, handlesFieldName = 'handles') {
11
7
  if (!instance) throw new TypeError('instance argument required');
12
8
 
13
9
  const messageTypes = Object.getPrototypeOf(instance).constructor[handlesFieldName];
@@ -21,5 +17,3 @@ function validateHandlers(instance, handlesFieldName = 'handles') {
21
17
  throw new Error(`'${type}' handler is not defined or not a function`);
22
18
  }
23
19
  }
24
-
25
- module.exports = validateHandlers;
package/index.d.ts DELETED
@@ -1,43 +0,0 @@
1
- export * from "./types/interfaces/IAggregate";
2
- export * from "./types/interfaces/IAggregateSnapshotStorage";
3
- export * from "./types/interfaces/ICommandBus";
4
- export * from "./types/interfaces/ICommandHandler";
5
- export * from "./types/interfaces/IConcurrentView";
6
- export * from "./types/interfaces/Identifier";
7
- export * from "./types/interfaces/IEventReceptor";
8
- export * from "./types/interfaces/IEventStorage";
9
- export * from "./types/interfaces/IEventStore";
10
- export * from "./types/interfaces/IEventStream";
11
- export * from "./types/interfaces/ILogger";
12
- export * from "./types/interfaces/IMessageBus";
13
- export * from "./types/interfaces/IObserver";
14
- export * from "./types/interfaces/IProjection";
15
- export * from "./types/interfaces/ISaga";
16
- export * from "./types/classes/AbstractAggregate";
17
- export * from "./types/classes/AbstractProjection";
18
- export * from "./types/classes/AbstractSaga";
19
- export * from "./types/classes/AggregateCommandHandler";
20
- export * from "./types/classes/CommandBus";
21
- export * from "./types/classes/CqrsContainerBuilder";
22
- export * from "./types/classes/EventStore";
23
- export * from "./types/classes/EventStream";
24
- export * from "./types/classes/SagaEventHandler";
25
-
26
- export var AbstractAggregate: typeof NodeCqrs.AbstractAggregate;
27
- export var AbstractProjection: typeof NodeCqrs.AbstractProjection;
28
- export var AbstractSaga: typeof NodeCqrs.AbstractSaga;
29
- export var AggregateCommandHandler: typeof NodeCqrs.AggregateCommandHandler;
30
- export var CommandBus: typeof NodeCqrs.CommandBus;
31
- export var ContainerBuilder: typeof NodeCqrs.CqrsContainerBuilder;
32
- export var EventStore: typeof NodeCqrs.EventStore;
33
- export var EventStream: typeof NodeCqrs.EventStream;
34
- export var InMemoryEventStorage: typeof NodeCqrs.InMemoryEventStorage;
35
- export var InMemoryMessageBus: typeof NodeCqrs.InMemoryMessageBus;
36
- export var InMemorySnapshotStorage: typeof NodeCqrs.InMemorySnapshotStorage;
37
- export var InMemoryView: typeof NodeCqrs.InMemoryView;
38
- export var SagaEventHandler: typeof NodeCqrs.SagaEventHandler;
39
-
40
- export {
41
- getMessageHandlerNames,
42
- subscribe
43
- } from "./src";
package/index.js DELETED
@@ -1,3 +0,0 @@
1
- 'use strict';
2
-
3
- module.exports = require('./src');
package/jsconfig.json DELETED
@@ -1,15 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "es6",
4
- "module": "commonjs",
5
- "allowSyntheticDefaultImports": true,
6
- "checkJs": true,
7
- "resolveJsonModule": true,
8
- "lib": [
9
- "es2018"
10
- ]
11
- },
12
- "exclude": [
13
- "node_modules"
14
- ]
15
- }