@meticoeus/ddd-es 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +43 -0
  3. package/dist/package.json +81 -0
  4. package/dist/src/AbstractEventProcessManager.d.ts +32 -0
  5. package/dist/src/AbstractEventProcessManager.d.ts.map +1 -0
  6. package/dist/src/AbstractEventProcessManager.js +142 -0
  7. package/dist/src/AbstractEventProcessManager.js.map +1 -0
  8. package/dist/src/AbstractEventProcessManager.mocks.d.ts +27 -0
  9. package/dist/src/AbstractEventProcessManager.mocks.d.ts.map +1 -0
  10. package/dist/src/AbstractEventProcessManager.mocks.js +86 -0
  11. package/dist/src/AbstractEventProcessManager.mocks.js.map +1 -0
  12. package/dist/src/AbstractEventProcessManager.test.d.ts +2 -0
  13. package/dist/src/AbstractEventProcessManager.test.d.ts.map +1 -0
  14. package/dist/src/AbstractEventProcessManager.test.js +109 -0
  15. package/dist/src/AbstractEventProcessManager.test.js.map +1 -0
  16. package/dist/src/AggregateRoot.d.ts +52 -0
  17. package/dist/src/AggregateRoot.d.ts.map +1 -0
  18. package/dist/src/AggregateRoot.fixtures.d.ts +4 -0
  19. package/dist/src/AggregateRoot.fixtures.d.ts.map +1 -0
  20. package/dist/src/AggregateRoot.fixtures.js +45 -0
  21. package/dist/src/AggregateRoot.fixtures.js.map +1 -0
  22. package/dist/src/AggregateRoot.js +136 -0
  23. package/dist/src/AggregateRoot.js.map +1 -0
  24. package/dist/src/AggregateRoot.mocks.d.ts +26 -0
  25. package/dist/src/AggregateRoot.mocks.d.ts.map +1 -0
  26. package/dist/src/AggregateRoot.mocks.js +82 -0
  27. package/dist/src/AggregateRoot.mocks.js.map +1 -0
  28. package/dist/src/EventProcessor.d.ts +40 -0
  29. package/dist/src/EventProcessor.d.ts.map +1 -0
  30. package/dist/src/EventProcessor.js +112 -0
  31. package/dist/src/EventProcessor.js.map +1 -0
  32. package/dist/src/EventProcessor.mocks.d.ts +11 -0
  33. package/dist/src/EventProcessor.mocks.d.ts.map +1 -0
  34. package/dist/src/EventProcessor.mocks.js +50 -0
  35. package/dist/src/EventProcessor.mocks.js.map +1 -0
  36. package/dist/src/EventProcessor.test.d.ts +2 -0
  37. package/dist/src/EventProcessor.test.d.ts.map +1 -0
  38. package/dist/src/EventProcessor.test.js +37 -0
  39. package/dist/src/EventProcessor.test.js.map +1 -0
  40. package/dist/src/IRepository.d.ts +14 -0
  41. package/dist/src/IRepository.d.ts.map +1 -0
  42. package/dist/src/IRepository.js +25 -0
  43. package/dist/src/IRepository.js.map +1 -0
  44. package/dist/src/Repository.d.ts +20 -0
  45. package/dist/src/Repository.d.ts.map +1 -0
  46. package/dist/src/Repository.js +127 -0
  47. package/dist/src/Repository.js.map +1 -0
  48. package/dist/src/Repository.mocks.d.ts +13 -0
  49. package/dist/src/Repository.mocks.d.ts.map +1 -0
  50. package/dist/src/Repository.mocks.js +55 -0
  51. package/dist/src/Repository.mocks.js.map +1 -0
  52. package/dist/src/Repository.test.d.ts +2 -0
  53. package/dist/src/Repository.test.d.ts.map +1 -0
  54. package/dist/src/Repository.test.js +108 -0
  55. package/dist/src/Repository.test.js.map +1 -0
  56. package/dist/src/entities.d.ts +37 -0
  57. package/dist/src/entities.d.ts.map +1 -0
  58. package/dist/src/entities.js +25 -0
  59. package/dist/src/entities.js.map +1 -0
  60. package/dist/src/index.d.ts +11 -0
  61. package/dist/src/index.d.ts.map +1 -0
  62. package/dist/src/index.js +34 -0
  63. package/dist/src/index.js.map +1 -0
  64. package/dist/src/logger.d.ts +23 -0
  65. package/dist/src/logger.d.ts.map +1 -0
  66. package/dist/src/logger.js +37 -0
  67. package/dist/src/logger.js.map +1 -0
  68. package/dist/src/mocks.d.ts +4 -0
  69. package/dist/src/mocks.d.ts.map +1 -0
  70. package/dist/src/mocks.js +27 -0
  71. package/dist/src/mocks.js.map +1 -0
  72. package/dist/src/otel/AbstractEventProcessManager.d.ts +6 -0
  73. package/dist/src/otel/AbstractEventProcessManager.d.ts.map +1 -0
  74. package/dist/src/otel/AbstractEventProcessManager.js +63 -0
  75. package/dist/src/otel/AbstractEventProcessManager.js.map +1 -0
  76. package/dist/src/otel/EventProcessor.d.ts +13 -0
  77. package/dist/src/otel/EventProcessor.d.ts.map +1 -0
  78. package/dist/src/otel/EventProcessor.js +59 -0
  79. package/dist/src/otel/EventProcessor.js.map +1 -0
  80. package/dist/src/otel/Repository.d.ts +12 -0
  81. package/dist/src/otel/Repository.d.ts.map +1 -0
  82. package/dist/src/otel/Repository.js +80 -0
  83. package/dist/src/otel/Repository.js.map +1 -0
  84. package/dist/src/otel/index.d.ts +6 -0
  85. package/dist/src/otel/index.d.ts.map +1 -0
  86. package/dist/src/otel/index.js +29 -0
  87. package/dist/src/otel/index.js.map +1 -0
  88. package/dist/src/otel/tracer.d.ts +2 -0
  89. package/dist/src/otel/tracer.d.ts.map +1 -0
  90. package/dist/src/otel/tracer.js +27 -0
  91. package/dist/src/otel/tracer.js.map +1 -0
  92. package/dist/src/otel/utils.d.ts +8 -0
  93. package/dist/src/otel/utils.d.ts.map +1 -0
  94. package/dist/src/otel/utils.js +70 -0
  95. package/dist/src/otel/utils.js.map +1 -0
  96. package/dist/src/schemas.d.ts +83 -0
  97. package/dist/src/schemas.d.ts.map +1 -0
  98. package/dist/src/schemas.js +77 -0
  99. package/dist/src/schemas.js.map +1 -0
  100. package/dist/src/schemas.test.d.ts +2 -0
  101. package/dist/src/schemas.test.d.ts.map +1 -0
  102. package/dist/src/schemas.test.js +110 -0
  103. package/dist/src/schemas.test.js.map +1 -0
  104. package/dist/src/types.d.ts +124 -0
  105. package/dist/src/types.d.ts.map +1 -0
  106. package/dist/src/types.fixtures.d.ts +3 -0
  107. package/dist/src/types.fixtures.d.ts.map +1 -0
  108. package/dist/src/types.fixtures.js +28 -0
  109. package/dist/src/types.fixtures.js.map +1 -0
  110. package/dist/src/types.js +130 -0
  111. package/dist/src/types.js.map +1 -0
  112. package/dist/src/types.mocks.d.ts +14 -0
  113. package/dist/src/types.mocks.d.ts.map +1 -0
  114. package/dist/src/types.mocks.js +239 -0
  115. package/dist/src/types.mocks.js.map +1 -0
  116. package/dist/src/utils.d.ts +11 -0
  117. package/dist/src/utils.d.ts.map +1 -0
  118. package/dist/src/utils.js +64 -0
  119. package/dist/src/utils.js.map +1 -0
  120. package/package.json +81 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Justin Bailey
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # **@meticoeus/ddd-es**
2
+
3
+ [![npm version](https://badge.fury.io/js/%40meticoeus%2Fddd-es.svg)](https://badge.fury.io/js/%40meticoeus%2Fddd-es)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+
7
+ *A TypeScript library for using Event Sourcing and Domain-Driven Design.*
8
+
9
+ ## **Overview**
10
+
11
+ `@meticoeus/ddd-es` is a TypeScript library that simplifies the implementation of Event Sourcing and Domain-Driven Design (DDD) patterns.
12
+
13
+ ## **Installation**
14
+
15
+ ```bash
16
+ npm install @meticoeus/ddd-es
17
+ ```
18
+
19
+ ## **Usage**
20
+
21
+ TODO
22
+
23
+ * * *
24
+
25
+ ## **Exports**
26
+
27
+ This library provides the following exports:
28
+
29
+ - **Core** (`import '@meticoeus/ddd-es'`): The main event-sourcing and DDD components.
30
+ - **Mocks** (`import '@meticoeus/ddd-es/mocks'`): Tools for testing and mocking events and the repositories.
31
+ - **OpenTelemetry** (`import '@meticoeus/ddd-es/otel'`): Optional OpenTelemetry integration for tracing events and commands.
32
+
33
+ ## **License**
34
+
35
+ This project is licensed under the MIT License - see the [LICENSE](./LICENSE) file for details.
36
+
37
+ ## **Contributing**
38
+
39
+ Contributions are welcome! Please open an issue or submit a pull request if you have improvements or bug fixes.
40
+
41
+ ## **Issues**
42
+
43
+ If you encounter any issues, please report them [here](https://github.com/yourusername/your-repo-name/issues).
@@ -0,0 +1,81 @@
1
+ {
2
+ "name": "@meticoeus/ddd-es",
3
+ "version": "0.2.0",
4
+ "description": "A Typescript library for using Event Sourcing and Domain Driven Design.",
5
+ "keywords": [
6
+ "DDD",
7
+ "Domain Driven Design",
8
+ "Event Sourcing"
9
+ ],
10
+ "author": "meticoeus",
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://github.com/meticoeus/ddd-es.git"
14
+ },
15
+ "bugs": {
16
+ "url": "https://github.com/meticoeus/ddd-es/issues"
17
+ },
18
+ "homepage": "https://github.com/meticoeus/ddd-es#readme",
19
+ "license": "MIT",
20
+ "type": "module",
21
+ "main": "./dist/src/index.js",
22
+ "types": "./dist/src/index.d.ts",
23
+ "exports": {
24
+ "./package.json": "./package.json",
25
+ ".": {
26
+ "types": "./dist/src/index.d.ts",
27
+ "import": "./dist/src/index.js"
28
+ },
29
+ "./mocks": {
30
+ "types": "./dist/src/mocks.d.ts",
31
+ "import": "./dist/src/mocks.js"
32
+ },
33
+ "./otel": {
34
+ "types": "./dist/src/otel/index.d.ts",
35
+ "import": "./dist/src/otel/index.js"
36
+ }
37
+ },
38
+ "files": [
39
+ "dist/",
40
+ "README.md",
41
+ "LICENSE"
42
+ ],
43
+ "engines": {
44
+ "node": ">=20.12.0"
45
+ },
46
+ "scripts": {
47
+ "test": "vitest",
48
+ "build": "tsc",
49
+ "format": "prettier --write .",
50
+ "postinstall": "husky install",
51
+ "prepare": "npm run build",
52
+ "prepublishOnly": "npm test -- --run"
53
+ },
54
+ "lint-staged": {
55
+ "**/*.ts": [
56
+ "npx prettier --write"
57
+ ]
58
+ },
59
+ "peerDependencies": {
60
+ "@opentelemetry/api": "^1.8.0"
61
+ },
62
+ "peerDependenciesMeta": {
63
+ "@opentelemetry/api": {
64
+ "optional": true
65
+ }
66
+ },
67
+ "dependencies": {
68
+ "zod": "^3.23.8"
69
+ },
70
+ "devDependencies": {
71
+ "@types/node": "^20.12.12",
72
+ "husky": "^8.0.0",
73
+ "lint-staged": "^15.2.5",
74
+ "pino": "^9.1.0",
75
+ "pino-pretty": "^11.0.0",
76
+ "prettier": "^3.2.5",
77
+ "prettier-plugin-organize-imports": "^3.2.4",
78
+ "typescript": "^5.4.5",
79
+ "vitest": "^1.6.0"
80
+ }
81
+ }
@@ -0,0 +1,32 @@
1
+ import { EventProcessor } from './EventProcessor.js';
2
+ import { IPersistedEvent } from './types.js';
3
+ export interface Subscribable<E> {
4
+ [Symbol.asyncIterator](): AsyncIterableIterator<E>;
5
+ }
6
+ export interface EventProcessManagerConfig {
7
+ /** max exponential backoff in milliseconds */
8
+ maxWaitTime: number;
9
+ }
10
+ export declare const DefaultEventProcessManagerConfig: EventProcessManagerConfig;
11
+ export declare abstract class AbstractEventProcessManager<DBEvent, ReadPosition, Subscription extends Subscribable<DBEvent>> {
12
+ protected readonly _processors: EventProcessor[];
13
+ protected _config: EventProcessManagerConfig;
14
+ protected _subscription: Subscription | undefined;
15
+ protected _processed: bigint;
16
+ protected _processorMap: Map<string, EventProcessor[]>;
17
+ protected _currentProcess: Promise<void> | undefined;
18
+ protected _checkpoint: ReadPosition;
19
+ constructor(_processors: EventProcessor[], initialCheckpoint: ReadPosition, config?: Partial<EventProcessManagerConfig>);
20
+ protected abstract startSubscription(checkpoint: ReadPosition): Subscription;
21
+ protected abstract endSubscription(sub: Subscription): void;
22
+ protected abstract getEventPosition(rawEvent: DBEvent): ReadPosition | undefined;
23
+ start(): Promise<void>;
24
+ stop(): Promise<void>;
25
+ protected handleDataEvent(event: IPersistedEvent, position?: ReadPosition): Promise<void>;
26
+ protected processEvent(event: IPersistedEvent, processor: EventProcessor, retryDelay: number): Promise<void>;
27
+ protected abstract extractEvent(rawEvent: DBEvent): IPersistedEvent | undefined;
28
+ protected getProcessors(event: IPersistedEvent): EventProcessor[] | undefined;
29
+ protected abstract getCheckpoint(): Promise<ReadPosition>;
30
+ protected abstract setCheckpoint(checkpoint: ReadPosition, processed: bigint): Promise<void>;
31
+ }
32
+ //# sourceMappingURL=AbstractEventProcessManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AbstractEventProcessManager.d.ts","sourceRoot":"","sources":["../../src/AbstractEventProcessManager.ts"],"names":[],"mappings":"AAwBA,OAAO,EAAE,cAAc,EAA2B,MAAM,qBAAqB,CAAA;AAE7E,OAAO,EAAwB,eAAe,EAAE,MAAM,YAAY,CAAA;AAGlE,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,qBAAqB,CAAC,CAAC,CAAC,CAAA;CACnD;AAED,MAAM,WAAW,yBAAyB;IACxC,8CAA8C;IAC9C,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,eAAO,MAAM,gCAAgC,EAAE,yBAE9C,CAAA;AAED,8BAAsB,2BAA2B,CAC/C,OAAO,EACP,YAAY,EACZ,YAAY,SAAS,YAAY,CAAC,OAAO,CAAC;IAUxC,SAAS,CAAC,QAAQ,CAAC,WAAW,EAAE,cAAc,EAAE;IARlD,SAAS,CAAC,OAAO,EAAE,yBAAyB,CAAA;IAC5C,SAAS,CAAC,aAAa,EAAE,YAAY,GAAG,SAAS,CAAA;IACjD,SAAS,CAAC,UAAU,EAAE,MAAM,CAAK;IACjC,SAAS,CAAC,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC,CAAY;IAClE,SAAS,CAAC,eAAe,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,SAAS,CAAA;IACpD,SAAS,CAAC,WAAW,EAAE,YAAY,CAAA;gBAGd,WAAW,EAAE,cAAc,EAAE,EAChD,iBAAiB,EAAE,YAAY,EAC/B,MAAM,GAAE,OAAO,CAAC,yBAAyB,CAAoC;IAU/E,SAAS,CAAC,QAAQ,CAAC,iBAAiB,CAAC,UAAU,EAAE,YAAY,GAAG,YAAY;IAC5E,SAAS,CAAC,QAAQ,CAAC,eAAe,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI;IAC3D,SAAS,CAAC,QAAQ,CAAC,gBAAgB,CAAC,QAAQ,EAAE,OAAO,GAAG,YAAY,GAAG,SAAS;IAEnE,KAAK;IAsBL,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;cAMlB,eAAe,CAAC,KAAK,EAAE,eAAe,EAAE,QAAQ,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;cA2B/E,YAAY,CAC1B,KAAK,EAAE,eAAe,EACtB,SAAS,EAAE,cAAc,EACzB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC;IAkChB,SAAS,CAAC,QAAQ,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,GAAG,eAAe,GAAG,SAAS;IAE/E,SAAS,CAAC,aAAa,CAAC,KAAK,EAAE,eAAe,GAAG,cAAc,EAAE,GAAG,SAAS;IAoB7E,SAAS,CAAC,QAAQ,CAAC,aAAa,IAAI,OAAO,CAAC,YAAY,CAAC;IAEzD,SAAS,CAAC,QAAQ,CAAC,aAAa,CAAC,UAAU,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAC7F"}
@@ -0,0 +1,142 @@
1
+ /*
2
+ MIT License
3
+
4
+ Copyright (c) 2024 Justin Bailey
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
23
+ */
24
+ import { EventProcessorException } from './EventProcessor.js';
25
+ import { logProvider } from './logger.js';
26
+ import { ConcurrencyException } from './types.js';
27
+ import { serializeBigIntJson } from './utils.js';
28
+ export const DefaultEventProcessManagerConfig = {
29
+ maxWaitTime: 5 * 60_1000, // 5 minutes
30
+ };
31
+ export class AbstractEventProcessManager {
32
+ _processors;
33
+ _config;
34
+ _subscription;
35
+ _processed = 0n;
36
+ _processorMap = new Map();
37
+ _currentProcess;
38
+ _checkpoint;
39
+ constructor(_processors, initialCheckpoint, config = DefaultEventProcessManagerConfig) {
40
+ this._processors = _processors;
41
+ this._checkpoint = initialCheckpoint;
42
+ this._config = config;
43
+ if (!this._config.maxWaitTime || this._config.maxWaitTime < 1000) {
44
+ this._config.maxWaitTime = DefaultEventProcessManagerConfig.maxWaitTime;
45
+ }
46
+ }
47
+ async start() {
48
+ const checkpoint = await this.getCheckpoint();
49
+ logProvider.log.debug(`Subscribing at checkpoint ${serializeBigIntJson(checkpoint)}`);
50
+ const subscription = this.startSubscription(checkpoint);
51
+ this._subscription = subscription;
52
+ for await (const rawEvent of subscription) {
53
+ try {
54
+ const event = this.extractEvent(rawEvent);
55
+ if (event) {
56
+ const position = this.getEventPosition(rawEvent);
57
+ this._currentProcess = this.handleDataEvent(event, position);
58
+ await this._currentProcess;
59
+ }
60
+ }
61
+ catch (err) {
62
+ throw err;
63
+ }
64
+ await this._currentProcess;
65
+ }
66
+ }
67
+ async stop() {
68
+ if (!this._subscription)
69
+ return;
70
+ this.endSubscription(this._subscription);
71
+ if (this._currentProcess)
72
+ await this._currentProcess;
73
+ }
74
+ async handleDataEvent(event, position) {
75
+ const processors = this.getProcessors(event) ?? [];
76
+ // const id = `${event?.revision}::${event?.streamId}`
77
+ // const msg = processors.length ? 'P' : 'F'
78
+ // const type = event?.type ?? 'no-type'
79
+ // logProvider.log.info(
80
+ // `[${msg}] Received event ${id}.${type} at ${event?.position?.commit}`,
81
+ // )
82
+ // track if we processed this event
83
+ let processed = false;
84
+ if (processors.length) {
85
+ for (const processor of processors) {
86
+ await this.processEvent(event, processor, 20);
87
+ }
88
+ processed = true;
89
+ }
90
+ // keep a running tally of number of events processed
91
+ this._processed++;
92
+ // if we have a position and either directly processed this event or have seen 32 new events, save the checkpoint
93
+ if (typeof position !== 'undefined' && (processed || this._processed % 32n === 0n)) {
94
+ await this.setCheckpoint(position, this._processed);
95
+ }
96
+ }
97
+ async processEvent(event, processor, retryDelay) {
98
+ const res = await processor.handle(event);
99
+ if (!res.ok) {
100
+ switch (true) {
101
+ case res.error instanceof ConcurrencyException:
102
+ // TODO: what can we do here? Should this actually happen?
103
+ break;
104
+ case res.error instanceof EventProcessorException:
105
+ switch (res.error.es.strategy) {
106
+ case 'retry':
107
+ await new Promise((resolve) => setTimeout(resolve, retryDelay));
108
+ const delay = Math.min(retryDelay + 20, this._config.maxWaitTime);
109
+ return this.processEvent(event, processor,
110
+ // vary the delay by a random +/- 10% to prevent synchronized requests against remote servers
111
+ delay * (Math.random() * 0.2 + 0.9));
112
+ case 'skip':
113
+ // ignore error and continue processing.
114
+ return;
115
+ case 'panic':
116
+ logProvider.log.fatal(`Unhandled exception processing ${event.id}: ${event.type}`, res.error);
117
+ throw res.error;
118
+ }
119
+ }
120
+ }
121
+ }
122
+ getProcessors(event) {
123
+ const type = event.type;
124
+ const list = this._processorMap.get(type);
125
+ if (list)
126
+ return list;
127
+ // lookup and cache the event processors
128
+ for (const processor of this._processors) {
129
+ if (processor.handlesEvent(type)) {
130
+ const list = this._processorMap.get(type);
131
+ if (list) {
132
+ list.push(processor);
133
+ }
134
+ else {
135
+ this._processorMap.set(type, [processor]);
136
+ }
137
+ }
138
+ }
139
+ return this._processorMap.get(type);
140
+ }
141
+ }
142
+ //# sourceMappingURL=AbstractEventProcessManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AbstractEventProcessManager.js","sourceRoot":"","sources":["../../src/AbstractEventProcessManager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import { AbstractEventProcessManager, Subscribable } from './AbstractEventProcessManager.js';
2
+ import { EventProcessor } from './EventProcessor.js';
3
+ import { IPersistedEvent } from './types.js';
4
+ export type MockReadPosition = 'Start' | 'End' | bigint;
5
+ export declare class MockSubscribable<E> implements Subscribable<E> {
6
+ private items;
7
+ private position;
8
+ private stopSignal;
9
+ constructor(items: E[], position?: MockReadPosition);
10
+ [Symbol.asyncIterator](): AsyncIterableIterator<E>;
11
+ stop(): void;
12
+ }
13
+ export declare class TestEventProcessManager extends AbstractEventProcessManager<IPersistedEvent, MockReadPosition, MockSubscribable<IPersistedEvent>> {
14
+ /** Events that will be processed by the mock when {@link AbstractEventProcessManager#start()} is called */
15
+ events: IPersistedEvent[];
16
+ mockCheckpoint: MockReadPosition;
17
+ constructor(
18
+ /** Events that will be processed by the mock when {@link AbstractEventProcessManager#start()} is called */
19
+ events: IPersistedEvent[], processors: EventProcessor[], initialCheckpoint: MockReadPosition);
20
+ protected extractEvent(rawEvent: IPersistedEvent): IPersistedEvent | undefined;
21
+ protected startSubscription(): MockSubscribable<IPersistedEvent>;
22
+ protected endSubscription(sub: MockSubscribable<IPersistedEvent>): void;
23
+ protected getEventPosition(rawEvent: IPersistedEvent): MockReadPosition | undefined;
24
+ protected getCheckpoint(): Promise<MockReadPosition>;
25
+ protected setCheckpoint(checkpoint: MockReadPosition): Promise<void>;
26
+ }
27
+ //# sourceMappingURL=AbstractEventProcessManager.mocks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AbstractEventProcessManager.mocks.d.ts","sourceRoot":"","sources":["../../src/AbstractEventProcessManager.mocks.ts"],"names":[],"mappings":"AAwBA,OAAO,EAAE,2BAA2B,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AAC5F,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAE5C,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,CAAA;AAEvD,qBAAa,gBAAgB,CAAC,CAAC,CAAE,YAAW,YAAY,CAAC,CAAC,CAAC;IAIvD,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,QAAQ;IAJlB,OAAO,CAAC,UAAU,CAAiB;gBAGzB,KAAK,EAAE,CAAC,EAAE,EACV,QAAQ,GAAE,gBAA0B;IAG9C,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,qBAAqB,CAAC,CAAC,CAAC;IAwBlD,IAAI;CAGL;AAED,qBAAa,uBAAwB,SAAQ,2BAA2B,CACtE,eAAe,EACf,gBAAgB,EAChB,gBAAgB,CAAC,eAAe,CAAC,CAClC;IAIG,2GAA2G;IACpG,MAAM,EAAE,eAAe,EAAE;IAJ3B,cAAc,EAAE,gBAAgB,CAAA;;IAGrC,2GAA2G;IACpG,MAAM,EAAE,eAAe,EAAE,EAChC,UAAU,EAAE,cAAc,EAAE,EAC5B,iBAAiB,EAAE,gBAAgB;cAOlB,YAAY,CAAC,QAAQ,EAAE,eAAe,GAAG,eAAe,GAAG,SAAS;cAIpE,iBAAiB,IAAI,gBAAgB,CAAC,eAAe,CAAC;cAItD,eAAe,CAAC,GAAG,EAAE,gBAAgB,CAAC,eAAe,CAAC,GAAG,IAAI;cAI7D,gBAAgB,CAAC,QAAQ,EAAE,eAAe,GAAG,gBAAgB,GAAG,SAAS;cAInE,aAAa,IAAI,OAAO,CAAC,gBAAgB,CAAC;cAI1C,aAAa,CAAC,UAAU,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;CAGpF"}
@@ -0,0 +1,86 @@
1
+ /*
2
+ MIT License
3
+
4
+ Copyright (c) 2024 Justin Bailey
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
23
+ */
24
+ import { AbstractEventProcessManager } from './AbstractEventProcessManager.js';
25
+ export class MockSubscribable {
26
+ items;
27
+ position;
28
+ stopSignal = false;
29
+ constructor(items, position = 'Start') {
30
+ this.items = items;
31
+ this.position = position;
32
+ }
33
+ [Symbol.asyncIterator]() {
34
+ const ctx = this;
35
+ let index = 0;
36
+ if (this.position === 'End') {
37
+ this.items.reverse();
38
+ }
39
+ if (typeof this.position === 'bigint') {
40
+ index = Number(this.position);
41
+ }
42
+ // Create an async generator function
43
+ async function* asyncGenerator() {
44
+ while (index < ctx.items.length) {
45
+ if (ctx.stopSignal)
46
+ return;
47
+ await new Promise((resolve) => setImmediate(resolve));
48
+ yield ctx.items[index++];
49
+ }
50
+ }
51
+ return asyncGenerator();
52
+ }
53
+ stop() {
54
+ this.stopSignal = true;
55
+ }
56
+ }
57
+ export class TestEventProcessManager extends AbstractEventProcessManager {
58
+ events;
59
+ mockCheckpoint;
60
+ constructor(
61
+ /** Events that will be processed by the mock when {@link AbstractEventProcessManager#start()} is called */
62
+ events, processors, initialCheckpoint) {
63
+ super(processors, initialCheckpoint);
64
+ this.events = events;
65
+ this.mockCheckpoint = initialCheckpoint;
66
+ }
67
+ extractEvent(rawEvent) {
68
+ return rawEvent;
69
+ }
70
+ startSubscription() {
71
+ return new MockSubscribable(this.events, this.mockCheckpoint);
72
+ }
73
+ endSubscription(sub) {
74
+ sub.stop();
75
+ }
76
+ getEventPosition(rawEvent) {
77
+ return rawEvent.revision;
78
+ }
79
+ async getCheckpoint() {
80
+ return this.mockCheckpoint;
81
+ }
82
+ async setCheckpoint(checkpoint) {
83
+ this.mockCheckpoint = checkpoint;
84
+ }
85
+ }
86
+ //# sourceMappingURL=AbstractEventProcessManager.mocks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AbstractEventProcessManager.mocks.js","sourceRoot":"","sources":["../../src/AbstractEventProcessManager.mocks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;EAsBE;AAEF,OAAO,EAAE,2BAA2B,EAAgB,MAAM,kCAAkC,CAAA;AAM5F,MAAM,OAAO,gBAAgB;IAIjB;IACA;IAJF,UAAU,GAAY,KAAK,CAAA;IAEnC,YACU,KAAU,EACV,WAA6B,OAAO;QADpC,UAAK,GAAL,KAAK,CAAK;QACV,aAAQ,GAAR,QAAQ,CAA4B;IAC3C,CAAC;IAEJ,CAAC,MAAM,CAAC,aAAa,CAAC;QACpB,MAAM,GAAG,GAAG,IAAI,CAAA;QAChB,IAAI,KAAK,GAAG,CAAC,CAAA;QAEb,IAAI,IAAI,CAAC,QAAQ,KAAK,KAAK,EAAE,CAAC;YAC5B,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAA;QACtB,CAAC;QACD,IAAI,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YACtC,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QAC/B,CAAC;QAED,qCAAqC;QACrC,KAAK,SAAS,CAAC,CAAC,cAAc;YAC5B,OAAO,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;gBAChC,IAAI,GAAG,CAAC,UAAU;oBAAE,OAAM;gBAE1B,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAA;gBACrD,MAAM,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,CAAM,CAAA;YAC/B,CAAC;QACH,CAAC;QAED,OAAO,cAAc,EAAE,CAAA;IACzB,CAAC;IAED,IAAI;QACF,IAAI,CAAC,UAAU,GAAG,IAAI,CAAA;IACxB,CAAC;CACF;AAED,MAAM,OAAO,uBAAwB,SAAQ,2BAI5C;IAKU;IAJF,cAAc,CAAkB;IAEvC;IACE,2GAA2G;IACpG,MAAyB,EAChC,UAA4B,EAC5B,iBAAmC;QAEnC,KAAK,CAAC,UAAU,EAAE,iBAAiB,CAAC,CAAA;QAJ7B,WAAM,GAAN,MAAM,CAAmB;QAMhC,IAAI,CAAC,cAAc,GAAG,iBAAiB,CAAA;IACzC,CAAC;IAEkB,YAAY,CAAC,QAAyB;QACvD,OAAO,QAAQ,CAAA;IACjB,CAAC;IAEkB,iBAAiB;QAClC,OAAO,IAAI,gBAAgB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,cAAc,CAAC,CAAA;IAC/D,CAAC;IAEkB,eAAe,CAAC,GAAsC;QACvE,GAAG,CAAC,IAAI,EAAE,CAAA;IACZ,CAAC;IAEkB,gBAAgB,CAAC,QAAyB;QAC3D,OAAO,QAAQ,CAAC,QAAQ,CAAA;IAC1B,CAAC;IAEkB,KAAK,CAAC,aAAa;QACpC,OAAO,IAAI,CAAC,cAAc,CAAA;IAC5B,CAAC;IAEkB,KAAK,CAAC,aAAa,CAAC,UAA4B;QACjE,IAAI,CAAC,cAAc,GAAG,UAAU,CAAA;IAClC,CAAC;CACF"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=AbstractEventProcessManager.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AbstractEventProcessManager.test.d.ts","sourceRoot":"","sources":["../../src/AbstractEventProcessManager.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,109 @@
1
+ /*
2
+ MIT License
3
+
4
+ Copyright (c) 2024 Justin Bailey
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
23
+ */
24
+ import { describe, expect, test, vi } from 'vitest';
25
+ import { TestEventProcessManager } from './AbstractEventProcessManager.mocks.js';
26
+ import { testCreated, testDeleted, testRenamed } from './AggregateRoot.fixtures.js';
27
+ import { TestEventProcessor, getEventStoreMock } from './EventProcessor.mocks.js';
28
+ describe('AbstractEventProcessManager', () => {
29
+ test.each([
30
+ {
31
+ msg: 'a single event',
32
+ end: 1n,
33
+ events: [testCreated],
34
+ },
35
+ {
36
+ msg: 'multiple events sequentially',
37
+ end: 3n,
38
+ events: [testCreated, testRenamed, testDeleted],
39
+ },
40
+ ])('processes multiple events sequentially', async ({ events, end }) => {
41
+ const eventStoreMock = getEventStoreMock();
42
+ const ep = new TestEventProcessor(eventStoreMock);
43
+ const epm = new TestEventProcessManager(events, [ep], 'Start');
44
+ await epm.start();
45
+ expect(epm.mockCheckpoint).toEqual(end);
46
+ });
47
+ test.each([
48
+ {
49
+ msg: 'for a single event',
50
+ end: 2n,
51
+ events: [testRenamed],
52
+ },
53
+ {
54
+ msg: 'and continue processing other events normally',
55
+ end: 3n,
56
+ events: [testCreated, testRenamed, testDeleted],
57
+ },
58
+ ])('retries on retryable process failure $msg', async ({ events, end }) => {
59
+ let called = 0;
60
+ const eventStoreMock = getEventStoreMock();
61
+ const ep = new TestEventProcessor(eventStoreMock);
62
+ const epm = new TestEventProcessManager(events, [ep], 'Start');
63
+ const handleTestRenamed = vi.fn(() => {
64
+ if (called < 3) {
65
+ called++;
66
+ const e = new Error('Connection failed');
67
+ e.code = 'ECONNRESET';
68
+ throw e;
69
+ }
70
+ });
71
+ ep._handlers.set('TestRenamed', handleTestRenamed);
72
+ await epm.start();
73
+ expect(epm.mockCheckpoint).toEqual(end);
74
+ expect(handleTestRenamed).toHaveBeenCalled();
75
+ expect(handleTestRenamed).toHaveBeenCalledTimes(4);
76
+ });
77
+ test('throws on unhandled process failure', async () => {
78
+ const events = [testCreated, testRenamed, testDeleted];
79
+ const eventStoreMock = getEventStoreMock();
80
+ const ep = new TestEventProcessor(eventStoreMock);
81
+ const epm = new TestEventProcessManager(events, [ep], 'Start');
82
+ const error = new Error('Something went wrong');
83
+ const handleTestCreated = vi.fn(() => undefined);
84
+ const handleTestRenamed = vi.fn(() => {
85
+ throw error;
86
+ });
87
+ const handleTestDeleted = vi.fn(() => undefined);
88
+ const mock = ep;
89
+ mock._handlers.set('TestCreated', handleTestCreated);
90
+ mock._handlers.set('TestRenamed', handleTestRenamed);
91
+ mock._handlers.set('TestDeleted', handleTestDeleted);
92
+ try {
93
+ await epm.start();
94
+ throw new Error('TestEventProcessManager#start() did not throw an error');
95
+ }
96
+ catch (e) {
97
+ const err = e;
98
+ expect(err.name).toEqual('EventProcessorException');
99
+ expect(err.type).toEqual(testRenamed.type);
100
+ expect(err.es.strategy).toEqual('panic');
101
+ expect(err.cause).toEqual(error);
102
+ expect(epm.mockCheckpoint).toEqual(1n);
103
+ expect(handleTestCreated).toHaveBeenCalled();
104
+ expect(handleTestRenamed).toHaveBeenCalled();
105
+ expect(handleTestDeleted).not.toHaveBeenCalled();
106
+ }
107
+ });
108
+ });
109
+ //# sourceMappingURL=AbstractEventProcessManager.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AbstractEventProcessManager.test.js","sourceRoot":"","sources":["../../src/AbstractEventProcessManager.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;EAsBE;AAEF,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AACnD,OAAO,EAAE,uBAAuB,EAAE,MAAM,wCAAwC,CAAA;AAChF,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAA;AAEnF,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAGjF,QAAQ,CAAC,6BAA6B,EAAE,GAAG,EAAE;IAC3C,IAAI,CAAC,IAAI,CAAC;QACR;YACE,GAAG,EAAE,gBAAgB;YACrB,GAAG,EAAE,EAAE;YACP,MAAM,EAAE,CAAC,WAAW,CAAC;SACtB;QACD;YACE,GAAG,EAAE,8BAA8B;YACnC,GAAG,EAAE,EAAE;YACP,MAAM,EAAE,CAAC,WAAW,EAAE,WAAW,EAAE,WAAW,CAAC;SAChD;KACF,CAAC,CACA,wCAAwC,EACxC,KAAK,EAAE,EAAE,MAAM,EAAE,GAAG,EAA2D,EAAE,EAAE;QACjF,MAAM,cAAc,GAAG,iBAAiB,EAAE,CAAA;QAC1C,MAAM,EAAE,GAAG,IAAI,kBAAkB,CAAC,cAAc,CAAC,CAAA;QACjD,MAAM,GAAG,GAAG,IAAI,uBAAuB,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,CAAA;QAC9D,MAAM,GAAG,CAAC,KAAK,EAAE,CAAA;QACjB,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IACzC,CAAC,CACF,CAAA;IAED,IAAI,CAAC,IAAI,CAAC;QACR;YACE,GAAG,EAAE,oBAAoB;YACzB,GAAG,EAAE,EAAE;YACP,MAAM,EAAE,CAAC,WAAW,CAAC;SACtB;QACD;YACE,GAAG,EAAE,+CAA+C;YACpD,GAAG,EAAE,EAAE;YACP,MAAM,EAAE,CAAC,WAAW,EAAE,WAAW,EAAE,WAAW,CAAC;SAChD;KACF,CAAC,CACA,2CAA2C,EAC3C,KAAK,EAAE,EAAE,MAAM,EAAE,GAAG,EAA2D,EAAE,EAAE;QACjF,IAAI,MAAM,GAAG,CAAC,CAAA;QACd,MAAM,cAAc,GAAG,iBAAiB,EAAE,CAAA;QAC1C,MAAM,EAAE,GAAG,IAAI,kBAAkB,CAAC,cAAc,CAAC,CAAA;QACjD,MAAM,GAAG,GAAG,IAAI,uBAAuB,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,CAAA;QAE9D,MAAM,iBAAiB,GAAG,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE;YACnC,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;gBACf,MAAM,EAAE,CAAA;gBACR,MAAM,CAAC,GAAG,IAAI,KAAK,CAAC,mBAAmB,CAAC,CACvC;gBAAC,CAAS,CAAC,IAAI,GAAG,YAAY,CAAA;gBAC/B,MAAM,CAAC,CAAA;YACT,CAAC;QACH,CAAC,CAAC,CACD;QAAC,EAAU,CAAC,SAAS,CAAC,GAAG,CAAC,aAAa,EAAE,iBAAiB,CAAC,CAAA;QAE5D,MAAM,GAAG,CAAC,KAAK,EAAE,CAAA;QACjB,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAEvC,MAAM,CAAC,iBAAiB,CAAC,CAAC,gBAAgB,EAAE,CAAA;QAC5C,MAAM,CAAC,iBAAiB,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAA;IACpD,CAAC,CACF,CAAA;IAED,IAAI,CAAC,qCAAqC,EAAE,KAAK,IAAI,EAAE;QACrD,MAAM,MAAM,GAAsB,CAAC,WAAW,EAAE,WAAW,EAAE,WAAW,CAAC,CAAA;QACzE,MAAM,cAAc,GAAG,iBAAiB,EAAE,CAAA;QAC1C,MAAM,EAAE,GAAG,IAAI,kBAAkB,CAAC,cAAc,CAAC,CAAA;QACjD,MAAM,GAAG,GAAG,IAAI,uBAAuB,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,CAAA;QAE9D,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAA;QAE/C,MAAM,iBAAiB,GAAG,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;QAChD,MAAM,iBAAiB,GAAG,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE;YACnC,MAAM,KAAK,CAAA;QACb,CAAC,CAAC,CAAA;QACF,MAAM,iBAAiB,GAAG,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;QAEhD,MAAM,IAAI,GAAG,EAAS,CAAA;QACtB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,aAAa,EAAE,iBAAiB,CAAC,CAAA;QACpD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,aAAa,EAAE,iBAAiB,CAAC,CAAA;QACpD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,aAAa,EAAE,iBAAiB,CAAC,CAAA;QAEpD,IAAI,CAAC;YACH,MAAM,GAAG,CAAC,KAAK,EAAE,CAAA;YACjB,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAA;QAC3E,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,GAAG,GAAG,CAA4B,CAAA;YACxC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,yBAAyB,CAAC,CAAA;YACnD,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;YAC1C,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;YACxC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;YAEhC,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;YACtC,MAAM,CAAC,iBAAiB,CAAC,CAAC,gBAAgB,EAAE,CAAA;YAC5C,MAAM,CAAC,iBAAiB,CAAC,CAAC,gBAAgB,EAAE,CAAA;YAC5C,MAAM,CAAC,iBAAiB,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAA;QAClD,CAAC;IACH,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
@@ -0,0 +1,52 @@
1
+ import { Exception, IEvent, IPersistedEvent, Persisted, Result } from './types.js';
2
+ export interface AggregateConfig {
3
+ /**
4
+ * Whether to cache the list of event the aggregate is loaded from.
5
+ * Required for Repository.save revision conflict management.
6
+ * Not required for read-only purposes.
7
+ * Defaults to true.
8
+ */
9
+ storeHistory?: boolean;
10
+ }
11
+ export type PersistedAggregate<DomainEvent extends IEvent, T extends AggregateRoot<DomainEvent>> = T & {
12
+ revision: bigint;
13
+ };
14
+ export interface AggregateRootClass<DomainEvent extends IEvent, T extends AggregateRoot<DomainEvent>> {
15
+ new (config?: AggregateConfig): T;
16
+ getStreamName(id: string): string;
17
+ eventsConflict(pending: IEvent, committed: IPersistedEvent): boolean;
18
+ }
19
+ export declare abstract class AggregateRoot<DomainEvent extends IEvent = IEvent> {
20
+ private __storeHistory;
21
+ private __history;
22
+ private __changes;
23
+ private readonly _appliers;
24
+ protected constructor(config?: AggregateConfig);
25
+ protected _id: string;
26
+ get id(): string;
27
+ protected _revision: bigint | undefined;
28
+ get revision(): bigint | undefined;
29
+ /** package scope. do not access externally */
30
+ get history(): Persisted<DomainEvent>[];
31
+ protected _deleted: boolean;
32
+ get deleted(): boolean;
33
+ static eventsConflict(pending: IEvent, committed: IPersistedEvent): boolean;
34
+ getUncommittedChanges(): DomainEvent[];
35
+ markChangesAsCommitted(): void;
36
+ loadFromHistory(history: Iterable<Persisted<DomainEvent>>): void;
37
+ protected applyChange(event: DomainEvent, isNew?: boolean): void;
38
+ /**
39
+ * Either return Ok() or return an error result representing the conflict.
40
+ */
41
+ hasConflict(_event: IEvent): Result<void, Exception>;
42
+ /**
43
+ * Get events that happened between after (exclusive) and to (inclusive).
44
+ * Set notation: (after, to]
45
+ * Requires config.storeHistory to be true or history will not be available.
46
+ *
47
+ * @param after The revision to start after.
48
+ * @param to Optional revision to end at (inclusive). Defaults to current revision.
49
+ */
50
+ getEventsDiff(after: bigint, to?: bigint): Persisted<DomainEvent>[];
51
+ }
52
+ //# sourceMappingURL=AggregateRoot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AggregateRoot.d.ts","sourceRoot":"","sources":["../../src/AggregateRoot.ts"],"names":[],"mappings":"AAyBA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,EAAM,SAAS,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAItF,MAAM,WAAW,eAAe;IAC9B;;;;;OAKG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAED,MAAM,MAAM,kBAAkB,CAC5B,WAAW,SAAS,MAAM,EAC1B,CAAC,SAAS,aAAa,CAAC,WAAW,CAAC,IAClC,CAAC,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAA;AAE5B,MAAM,WAAW,kBAAkB,CACjC,WAAW,SAAS,MAAM,EAC1B,CAAC,SAAS,aAAa,CAAC,WAAW,CAAC;IAEpC,KAAK,MAAM,CAAC,EAAE,eAAe,GAAG,CAAC,CAAA;IAEjC,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;IAEjC,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,eAAe,GAAG,OAAO,CAAA;CACrE;AAED,8BAAsB,aAAa,CAAC,WAAW,SAAS,MAAM,GAAG,MAAM;IACrE,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,SAAS,CAA+B;IAChD,OAAO,CAAC,SAAS,CAAoB;IACrC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAmD;IAE7E,SAAS,aAAa,MAAM,CAAC,EAAE,eAAe;IAa9C,SAAS,CAAC,GAAG,EAAE,MAAM,CAAA;IAErB,IAAW,EAAE,IAAI,MAAM,CAEtB;IAED,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;IAEvC,IAAW,QAAQ,IAAI,MAAM,GAAG,SAAS,CAExC;IAED,8CAA8C;IAC9C,IAAW,OAAO,IAAI,SAAS,CAAC,WAAW,CAAC,EAAE,CAE7C;IAED,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAQ;IAEnC,IAAW,OAAO,IAAI,OAAO,CAE5B;WAEa,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,eAAe,GAAG,OAAO;IAM3E,qBAAqB,IAAI,WAAW,EAAE;IAItC,sBAAsB,IAAI,IAAI;IAI9B,eAAe,CAAC,OAAO,EAAE,QAAQ,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,GAAG,IAAI;IAYvE,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,GAAE,OAAc,GAAG,IAAI;IAiBtE;;OAEG;IACI,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC;IAK3D;;;;;;;OAOG;IACI,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC,WAAW,CAAC,EAAE;CAiB3E"}
@@ -0,0 +1,4 @@
1
+ export declare const testCreated: import("./types.js").Persisted<import("./types.js").IEvent<string, import("./types.js").DataType, import("./types.js").EventMetadata>>;
2
+ export declare const testRenamed: import("./types.js").Persisted<import("./types.js").IEvent<string, import("./types.js").DataType, import("./types.js").EventMetadata>>;
3
+ export declare const testDeleted: import("./types.js").Persisted<import("./types.js").IEvent<string, import("./types.js").DataType, import("./types.js").EventMetadata>>;
4
+ //# sourceMappingURL=AggregateRoot.fixtures.d.ts.map