node-cqrs 1.0.0 → 1.1.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -1
- package/README.md +115 -101
- package/dist/cjs/AbstractProjection.js +43 -18
- package/dist/cjs/AbstractProjection.js.map +1 -1
- package/dist/cjs/AggregateCommandHandler.js +27 -13
- package/dist/cjs/AggregateCommandHandler.js.map +1 -1
- package/dist/cjs/CqrsContainerBuilder.js +6 -1
- package/dist/cjs/CqrsContainerBuilder.js.map +1 -1
- package/dist/cjs/EventDispatchPipeline.js +12 -2
- package/dist/cjs/EventDispatchPipeline.js.map +1 -1
- package/dist/cjs/EventDispatcher.js +39 -10
- package/dist/cjs/EventDispatcher.js.map +1 -1
- package/dist/cjs/EventStore.js +7 -2
- package/dist/cjs/EventStore.js.map +1 -1
- package/dist/cjs/SagaEventHandler.js +54 -40
- package/dist/cjs/SagaEventHandler.js.map +1 -1
- package/dist/cjs/in-memory/InMemoryEventStorage.js +39 -24
- package/dist/cjs/in-memory/InMemoryEventStorage.js.map +1 -1
- package/dist/cjs/in-memory/InMemoryMessageBus.js +11 -5
- package/dist/cjs/in-memory/InMemoryMessageBus.js.map +1 -1
- package/dist/cjs/interfaces/IDispatchPipelineProcessor.js.map +1 -1
- package/dist/cjs/interfaces/IMessageMeta.js +3 -0
- package/dist/cjs/interfaces/IMessageMeta.js.map +1 -0
- package/dist/cjs/interfaces/IObservable.js.map +1 -1
- package/dist/cjs/interfaces/index.js +1 -0
- package/dist/cjs/interfaces/index.js.map +1 -1
- package/dist/cjs/mongodb/AbstractMongoAccessor.js +51 -0
- package/dist/cjs/mongodb/AbstractMongoAccessor.js.map +1 -0
- package/dist/cjs/mongodb/AbstractMongoObjectProjection.js +26 -0
- package/dist/cjs/mongodb/AbstractMongoObjectProjection.js.map +1 -0
- package/dist/cjs/mongodb/AbstractMongoView.js +57 -0
- package/dist/cjs/mongodb/AbstractMongoView.js.map +1 -0
- package/dist/cjs/mongodb/IContainer.js +3 -0
- package/dist/cjs/mongodb/IContainer.js.map +1 -0
- package/dist/cjs/mongodb/MongoEventLocker.js +104 -0
- package/dist/cjs/mongodb/MongoEventLocker.js.map +1 -0
- package/dist/cjs/mongodb/MongoEventStorage.js +200 -0
- package/dist/cjs/mongodb/MongoEventStorage.js.map +1 -0
- package/dist/cjs/mongodb/MongoObjectStorage.js +101 -0
- package/dist/cjs/mongodb/MongoObjectStorage.js.map +1 -0
- package/dist/cjs/mongodb/MongoObjectView.js +41 -0
- package/dist/cjs/mongodb/MongoObjectView.js.map +1 -0
- package/dist/cjs/mongodb/MongoProjectionDataParams.js +3 -0
- package/dist/cjs/mongodb/MongoProjectionDataParams.js.map +1 -0
- package/dist/cjs/mongodb/MongoViewLocker.js +136 -0
- package/dist/cjs/mongodb/MongoViewLocker.js.map +1 -0
- package/dist/cjs/mongodb/index.js +28 -0
- package/dist/cjs/mongodb/index.js.map +1 -0
- package/dist/cjs/mongodb/registerExitCleanup.js +28 -0
- package/dist/cjs/mongodb/registerExitCleanup.js.map +1 -0
- package/dist/cjs/mongodb/utils/getEventId.js +14 -0
- package/dist/cjs/mongodb/utils/getEventId.js.map +1 -0
- package/dist/cjs/mongodb/utils/index.js +18 -0
- package/dist/cjs/mongodb/utils/index.js.map +1 -0
- package/dist/cjs/rabbitmq/RabbitMqCommandBus.js +21 -8
- package/dist/cjs/rabbitmq/RabbitMqCommandBus.js.map +1 -1
- package/dist/cjs/rabbitmq/RabbitMqEventBus.js +2 -2
- package/dist/cjs/rabbitmq/RabbitMqEventBus.js.map +1 -1
- package/dist/cjs/rabbitmq/RabbitMqGateway.js +89 -64
- package/dist/cjs/rabbitmq/RabbitMqGateway.js.map +1 -1
- package/dist/cjs/redis/AbstractRedisAccessor.js +51 -0
- package/dist/cjs/redis/AbstractRedisAccessor.js.map +1 -0
- package/dist/cjs/redis/AbstractRedisProjection.js +26 -0
- package/dist/cjs/redis/AbstractRedisProjection.js.map +1 -0
- package/dist/cjs/redis/IContainer.js +3 -0
- package/dist/cjs/redis/IContainer.js.map +1 -0
- package/dist/cjs/redis/RedisEventLocker.js +96 -0
- package/dist/cjs/redis/RedisEventLocker.js.map +1 -0
- package/dist/cjs/redis/RedisObjectStorage.js +125 -0
- package/dist/cjs/redis/RedisObjectStorage.js.map +1 -0
- package/dist/cjs/redis/RedisProjectionDataParams.js +3 -0
- package/dist/cjs/redis/RedisProjectionDataParams.js.map +1 -0
- package/dist/cjs/redis/RedisView.js +81 -0
- package/dist/cjs/redis/RedisView.js.map +1 -0
- package/dist/cjs/redis/RedisViewLocker.js +111 -0
- package/dist/cjs/redis/RedisViewLocker.js.map +1 -0
- package/dist/cjs/redis/index.js +30 -0
- package/dist/cjs/redis/index.js.map +1 -0
- package/dist/cjs/redis/utils/getEventId.js +14 -0
- package/dist/cjs/redis/utils/getEventId.js.map +1 -0
- package/dist/cjs/redis/utils/index.js +18 -0
- package/dist/cjs/redis/utils/index.js.map +1 -0
- package/dist/cjs/sqlite/AbstractSqliteView.js.map +1 -1
- package/dist/cjs/sqlite/SqliteEventStorage.js +215 -0
- package/dist/cjs/sqlite/SqliteEventStorage.js.map +1 -0
- package/dist/cjs/sqlite/SqliteObjectStorage.js +6 -6
- package/dist/cjs/sqlite/SqliteObjectStorage.js.map +1 -1
- package/dist/cjs/sqlite/SqliteObjectView.js.map +1 -1
- package/dist/cjs/sqlite/index.js +1 -0
- package/dist/cjs/sqlite/index.js.map +1 -1
- package/dist/cjs/sqlite/utils/bufferToGuid.js +9 -0
- package/dist/cjs/sqlite/utils/bufferToGuid.js.map +1 -0
- package/dist/cjs/sqlite/utils/getEventId.js +2 -5
- package/dist/cjs/sqlite/utils/getEventId.js.map +1 -1
- package/dist/cjs/sqlite/utils/guid.js +1 -1
- package/dist/cjs/sqlite/utils/guid.js.map +1 -1
- package/dist/cjs/sqlite/utils/index.js +1 -0
- package/dist/cjs/sqlite/utils/index.js.map +1 -1
- package/dist/cjs/telemetry/index.js +20 -0
- package/dist/cjs/telemetry/index.js.map +1 -0
- package/dist/cjs/telemetry/recordSpanError.js +19 -0
- package/dist/cjs/telemetry/recordSpanError.js.map +1 -0
- package/dist/cjs/telemetry/spanAttributes.js +26 -0
- package/dist/cjs/telemetry/spanAttributes.js.map +1 -0
- package/dist/cjs/telemetry/spanContext.js +25 -0
- package/dist/cjs/telemetry/spanContext.js.map +1 -0
- package/dist/cjs/utils/MapAssertable.js +25 -1
- package/dist/cjs/utils/MapAssertable.js.map +1 -1
- package/dist/esm/AbstractProjection.js +32 -7
- package/dist/esm/AbstractProjection.js.map +1 -1
- package/dist/esm/AggregateCommandHandler.js +22 -8
- package/dist/esm/AggregateCommandHandler.js.map +1 -1
- package/dist/esm/CqrsContainerBuilder.js +6 -1
- package/dist/esm/CqrsContainerBuilder.js.map +1 -1
- package/dist/esm/EventDispatchPipeline.js +12 -2
- package/dist/esm/EventDispatchPipeline.js.map +1 -1
- package/dist/esm/EventDispatcher.js +33 -4
- package/dist/esm/EventDispatcher.js.map +1 -1
- package/dist/esm/EventStore.js +7 -2
- package/dist/esm/EventStore.js.map +1 -1
- package/dist/esm/SagaEventHandler.js +42 -28
- package/dist/esm/SagaEventHandler.js.map +1 -1
- package/dist/esm/in-memory/InMemoryEventStorage.js +25 -10
- package/dist/esm/in-memory/InMemoryEventStorage.js.map +1 -1
- package/dist/esm/in-memory/InMemoryMessageBus.js +11 -5
- package/dist/esm/in-memory/InMemoryMessageBus.js.map +1 -1
- package/dist/esm/interfaces/IDispatchPipelineProcessor.js.map +1 -1
- package/dist/esm/interfaces/IMessageMeta.js +2 -0
- package/dist/esm/interfaces/IMessageMeta.js.map +1 -0
- package/dist/esm/interfaces/IObservable.js.map +1 -1
- package/dist/esm/interfaces/index.js +1 -0
- package/dist/esm/interfaces/index.js.map +1 -1
- package/dist/esm/mongodb/AbstractMongoAccessor.js +47 -0
- package/dist/esm/mongodb/AbstractMongoAccessor.js.map +1 -0
- package/dist/esm/mongodb/AbstractMongoObjectProjection.js +22 -0
- package/dist/esm/mongodb/AbstractMongoObjectProjection.js.map +1 -0
- package/dist/esm/mongodb/AbstractMongoView.js +53 -0
- package/dist/esm/mongodb/AbstractMongoView.js.map +1 -0
- package/dist/esm/mongodb/IContainer.js +2 -0
- package/dist/esm/mongodb/IContainer.js.map +1 -0
- package/dist/esm/mongodb/MongoEventLocker.js +100 -0
- package/dist/esm/mongodb/MongoEventLocker.js.map +1 -0
- package/dist/esm/mongodb/MongoEventStorage.js +196 -0
- package/dist/esm/mongodb/MongoEventStorage.js.map +1 -0
- package/dist/esm/mongodb/MongoObjectStorage.js +97 -0
- package/dist/esm/mongodb/MongoObjectStorage.js.map +1 -0
- package/dist/esm/mongodb/MongoObjectView.js +37 -0
- package/dist/esm/mongodb/MongoObjectView.js.map +1 -0
- package/dist/esm/mongodb/MongoProjectionDataParams.js +2 -0
- package/dist/esm/mongodb/MongoProjectionDataParams.js.map +1 -0
- package/dist/esm/mongodb/MongoViewLocker.js +132 -0
- package/dist/esm/mongodb/MongoViewLocker.js.map +1 -0
- package/dist/esm/mongodb/index.js +12 -0
- package/dist/esm/mongodb/index.js.map +1 -0
- package/dist/esm/mongodb/registerExitCleanup.js +24 -0
- package/dist/esm/mongodb/registerExitCleanup.js.map +1 -0
- package/dist/esm/mongodb/utils/getEventId.js +10 -0
- package/dist/esm/mongodb/utils/getEventId.js.map +1 -0
- package/dist/esm/mongodb/utils/index.js +2 -0
- package/dist/esm/mongodb/utils/index.js.map +1 -0
- package/dist/esm/rabbitmq/RabbitMqCommandBus.js +21 -8
- package/dist/esm/rabbitmq/RabbitMqCommandBus.js.map +1 -1
- package/dist/esm/rabbitmq/RabbitMqEventBus.js +2 -2
- package/dist/esm/rabbitmq/RabbitMqEventBus.js.map +1 -1
- package/dist/esm/rabbitmq/RabbitMqGateway.js +69 -44
- package/dist/esm/rabbitmq/RabbitMqGateway.js.map +1 -1
- package/dist/esm/redis/AbstractRedisAccessor.js +47 -0
- package/dist/esm/redis/AbstractRedisAccessor.js.map +1 -0
- package/dist/esm/redis/AbstractRedisProjection.js +22 -0
- package/dist/esm/redis/AbstractRedisProjection.js.map +1 -0
- package/dist/esm/redis/IContainer.js +2 -0
- package/dist/esm/redis/IContainer.js.map +1 -0
- package/dist/esm/redis/RedisEventLocker.js +92 -0
- package/dist/esm/redis/RedisEventLocker.js.map +1 -0
- package/dist/esm/redis/RedisObjectStorage.js +121 -0
- package/dist/esm/redis/RedisObjectStorage.js.map +1 -0
- package/dist/esm/redis/RedisProjectionDataParams.js +2 -0
- package/dist/esm/redis/RedisProjectionDataParams.js.map +1 -0
- package/dist/esm/redis/RedisView.js +77 -0
- package/dist/esm/redis/RedisView.js.map +1 -0
- package/dist/esm/redis/RedisViewLocker.js +107 -0
- package/dist/esm/redis/RedisViewLocker.js.map +1 -0
- package/dist/esm/redis/index.js +14 -0
- package/dist/esm/redis/index.js.map +1 -0
- package/dist/esm/redis/utils/getEventId.js +10 -0
- package/dist/esm/redis/utils/getEventId.js.map +1 -0
- package/dist/esm/redis/utils/index.js +2 -0
- package/dist/esm/redis/utils/index.js.map +1 -0
- package/dist/esm/sqlite/AbstractSqliteView.js.map +1 -1
- package/dist/esm/sqlite/SqliteEventStorage.js +211 -0
- package/dist/esm/sqlite/SqliteEventStorage.js.map +1 -0
- package/dist/esm/sqlite/SqliteObjectStorage.js +7 -7
- package/dist/esm/sqlite/SqliteObjectStorage.js.map +1 -1
- package/dist/esm/sqlite/SqliteObjectView.js.map +1 -1
- package/dist/esm/sqlite/index.js +1 -0
- package/dist/esm/sqlite/index.js.map +1 -1
- package/dist/esm/sqlite/utils/bufferToGuid.js +5 -0
- package/dist/esm/sqlite/utils/bufferToGuid.js.map +1 -0
- package/dist/esm/sqlite/utils/getEventId.js +2 -2
- package/dist/esm/sqlite/utils/getEventId.js.map +1 -1
- package/dist/esm/sqlite/utils/guid.js +1 -1
- package/dist/esm/sqlite/utils/guid.js.map +1 -1
- package/dist/esm/sqlite/utils/index.js +1 -0
- package/dist/esm/sqlite/utils/index.js.map +1 -1
- package/dist/esm/telemetry/index.js +4 -0
- package/dist/esm/telemetry/index.js.map +1 -0
- package/dist/esm/telemetry/recordSpanError.js +16 -0
- package/dist/esm/telemetry/recordSpanError.js.map +1 -0
- package/dist/esm/telemetry/spanAttributes.js +23 -0
- package/dist/esm/telemetry/spanAttributes.js.map +1 -0
- package/dist/esm/telemetry/spanContext.js +22 -0
- package/dist/esm/telemetry/spanContext.js.map +1 -0
- package/dist/esm/utils/MapAssertable.js +25 -1
- package/dist/esm/utils/MapAssertable.js.map +1 -1
- package/dist/types/AbstractProjection.d.ts +3 -1
- package/dist/types/AggregateCommandHandler.d.ts +3 -3
- package/dist/types/EventDispatchPipeline.d.ts +3 -1
- package/dist/types/EventDispatcher.d.ts +9 -1
- package/dist/types/EventStore.d.ts +4 -2
- package/dist/types/SagaEventHandler.d.ts +3 -3
- package/dist/types/in-memory/InMemoryEventStorage.d.ts +2 -1
- package/dist/types/in-memory/InMemoryMessageBus.d.ts +3 -3
- package/dist/types/interfaces/ICommandBus.d.ts +3 -2
- package/dist/types/interfaces/IContainer.d.ts +7 -0
- package/dist/types/interfaces/IDispatchPipelineProcessor.d.ts +2 -0
- package/dist/types/interfaces/IEventDispatcher.d.ts +3 -0
- package/dist/types/interfaces/IMessageMeta.d.ts +4 -0
- package/dist/types/interfaces/IObservable.d.ts +2 -1
- package/dist/types/interfaces/index.d.ts +1 -0
- package/dist/types/mongodb/AbstractMongoAccessor.d.ts +26 -0
- package/dist/types/mongodb/AbstractMongoObjectProjection.d.ts +8 -0
- package/dist/types/mongodb/AbstractMongoView.d.ts +25 -0
- package/dist/types/mongodb/IContainer.d.ts +11 -0
- package/dist/types/mongodb/MongoEventLocker.d.ts +47 -0
- package/dist/types/mongodb/MongoEventStorage.d.ts +27 -0
- package/dist/types/mongodb/MongoObjectStorage.d.ts +26 -0
- package/dist/types/mongodb/MongoObjectView.d.ts +16 -0
- package/dist/types/mongodb/MongoProjectionDataParams.d.ts +14 -0
- package/dist/types/mongodb/MongoViewLocker.d.ts +43 -0
- package/dist/types/mongodb/index.d.ts +11 -0
- package/dist/types/mongodb/registerExitCleanup.d.ts +10 -0
- package/dist/types/mongodb/utils/getEventId.d.ts +5 -0
- package/dist/types/mongodb/utils/index.d.ts +1 -0
- package/dist/types/rabbitmq/IContainer.d.ts +2 -2
- package/dist/types/rabbitmq/RabbitMqCommandBus.d.ts +5 -4
- package/dist/types/rabbitmq/RabbitMqEventBus.d.ts +2 -2
- package/dist/types/rabbitmq/RabbitMqGateway.d.ts +4 -4
- package/dist/types/redis/AbstractRedisAccessor.d.ts +26 -0
- package/dist/types/redis/AbstractRedisProjection.d.ts +8 -0
- package/dist/types/redis/IContainer.d.ts +7 -0
- package/dist/types/redis/RedisEventLocker.d.ts +36 -0
- package/dist/types/redis/RedisObjectStorage.d.ts +26 -0
- package/dist/types/redis/RedisProjectionDataParams.d.ts +21 -0
- package/dist/types/redis/RedisView.d.ts +33 -0
- package/dist/types/redis/RedisViewLocker.d.ts +35 -0
- package/dist/types/redis/index.d.ts +13 -0
- package/dist/types/redis/utils/getEventId.d.ts +5 -0
- package/dist/types/redis/utils/index.d.ts +1 -0
- package/dist/types/sqlite/AbstractSqliteView.d.ts +2 -2
- package/dist/types/sqlite/SqliteEventStorage.d.ts +18 -0
- package/dist/types/sqlite/SqliteObjectStorage.d.ts +7 -7
- package/dist/types/sqlite/SqliteObjectView.d.ts +7 -7
- package/dist/types/sqlite/index.d.ts +1 -0
- package/dist/types/sqlite/utils/bufferToGuid.d.ts +4 -0
- package/dist/types/sqlite/utils/getEventId.d.ts +1 -1
- package/dist/types/sqlite/utils/guid.d.ts +2 -1
- package/dist/types/sqlite/utils/index.d.ts +1 -0
- package/dist/types/telemetry/index.d.ts +3 -0
- package/dist/types/telemetry/recordSpanError.d.ts +6 -0
- package/dist/types/telemetry/spanAttributes.d.ts +14 -0
- package/dist/types/telemetry/spanContext.d.ts +12 -0
- package/dist/types/utils/MapAssertable.d.ts +8 -0
- package/package.json +43 -13
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { assertDefined, assertFunction, assertNonNegativeInteger, assertString } from "../utils/assert.js";
|
|
2
|
+
import { AbstractMongoAccessor } from "./AbstractMongoAccessor.js";
|
|
3
|
+
/**
|
|
4
|
+
* MongoDB-backed implementation of IObjectStorage.
|
|
5
|
+
*
|
|
6
|
+
* Each record is stored as a document `{ _id, data, version }`.
|
|
7
|
+
* The version field enables optimistic concurrency control: `update` and
|
|
8
|
+
* `updateEnforcingNew` re-read the record after the user callback runs and
|
|
9
|
+
* atomically commit only when the version still matches.
|
|
10
|
+
* On mismatch the operation retries up to `maxRetries` times.
|
|
11
|
+
*/
|
|
12
|
+
export class MongoObjectStorage extends AbstractMongoAccessor {
|
|
13
|
+
#tableName;
|
|
14
|
+
#maxRetries;
|
|
15
|
+
#collection;
|
|
16
|
+
constructor(o) {
|
|
17
|
+
super(o);
|
|
18
|
+
assertString(o.tableName, 'tableName');
|
|
19
|
+
if (o.maxRetries !== undefined)
|
|
20
|
+
assertNonNegativeInteger(o.maxRetries, 'maxRetries');
|
|
21
|
+
this.#tableName = o.tableName;
|
|
22
|
+
this.#maxRetries = o.maxRetries ?? 100;
|
|
23
|
+
}
|
|
24
|
+
async initialize(db) {
|
|
25
|
+
this.#collection = db.collection(this.#tableName);
|
|
26
|
+
}
|
|
27
|
+
async get(id) {
|
|
28
|
+
assertDefined(id, 'id');
|
|
29
|
+
await this.assertConnection();
|
|
30
|
+
const doc = await this.#collection.findOne({ _id: String(id) });
|
|
31
|
+
if (!doc)
|
|
32
|
+
return undefined;
|
|
33
|
+
return doc.data;
|
|
34
|
+
}
|
|
35
|
+
async create(id, data) {
|
|
36
|
+
assertDefined(id, 'id');
|
|
37
|
+
await this.assertConnection();
|
|
38
|
+
try {
|
|
39
|
+
await this.#collection.insertOne({ _id: String(id), data, version: 1 });
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
if (typeof err === 'object' && err !== null && 'code' in err && err.code === 11000)
|
|
43
|
+
throw new Error(`Record '${id}' could not be created`);
|
|
44
|
+
throw err;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async update(id, update) {
|
|
48
|
+
assertDefined(id, 'id');
|
|
49
|
+
assertFunction(update, 'update');
|
|
50
|
+
await this.assertConnection();
|
|
51
|
+
for (let attempt = 0; attempt <= this.#maxRetries; attempt++) {
|
|
52
|
+
const doc = await this.#collection.findOne({ _id: String(id) });
|
|
53
|
+
if (!doc)
|
|
54
|
+
throw new Error(`Record '${id}' does not exist`);
|
|
55
|
+
const updatedData = update(doc.data);
|
|
56
|
+
const result = await this.#collection.findOneAndUpdate({ _id: String(id), version: doc.version }, { $set: { data: updatedData, version: doc.version + 1 } });
|
|
57
|
+
if (result)
|
|
58
|
+
return;
|
|
59
|
+
// version mismatch — retry
|
|
60
|
+
}
|
|
61
|
+
throw new Error(`Record '${id}' could not be updated after ${this.#maxRetries} retries`);
|
|
62
|
+
}
|
|
63
|
+
async updateEnforcingNew(id, update) {
|
|
64
|
+
assertDefined(id, 'id');
|
|
65
|
+
assertFunction(update, 'update');
|
|
66
|
+
await this.assertConnection();
|
|
67
|
+
for (let attempt = 0; attempt <= this.#maxRetries; attempt++) {
|
|
68
|
+
const doc = await this.#collection.findOne({ _id: String(id) });
|
|
69
|
+
if (doc) {
|
|
70
|
+
const updatedData = update(doc.data);
|
|
71
|
+
const result = await this.#collection.findOneAndUpdate({ _id: String(id), version: doc.version }, { $set: { data: updatedData, version: doc.version + 1 } });
|
|
72
|
+
if (result)
|
|
73
|
+
return;
|
|
74
|
+
// version mismatch — retry
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
try {
|
|
78
|
+
await this.#collection.insertOne({ _id: String(id), data: update(undefined), version: 1 });
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
if (typeof err === 'object' && err !== null && 'code' in err && err.code === 11000)
|
|
83
|
+
continue; // Another process inserted first — retry
|
|
84
|
+
throw err;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
throw new Error(`Record '${id}' could not be upserted after ${this.#maxRetries} retries`);
|
|
89
|
+
}
|
|
90
|
+
async delete(id) {
|
|
91
|
+
assertDefined(id, 'id');
|
|
92
|
+
await this.assertConnection();
|
|
93
|
+
const result = await this.#collection.deleteOne({ _id: String(id) });
|
|
94
|
+
return result.deletedCount === 1;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=MongoObjectStorage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MongoObjectStorage.js","sourceRoot":"","sources":["../../../src/mongodb/MongoObjectStorage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { AbstractMongoView } from "./AbstractMongoView.js";
|
|
2
|
+
import { MongoObjectStorage } from "./MongoObjectStorage.js";
|
|
3
|
+
import { assertString } from "../utils/assert.js";
|
|
4
|
+
/**
|
|
5
|
+
* MongoDB-backed object view with restore locking and last-processed-event tracking
|
|
6
|
+
*/
|
|
7
|
+
export class MongoObjectView extends AbstractMongoView {
|
|
8
|
+
#mongoObjectStorage;
|
|
9
|
+
constructor(options) {
|
|
10
|
+
assertString(options?.tableNamePrefix, 'tableNamePrefix');
|
|
11
|
+
assertString(options?.schemaVersion, 'schemaVersion');
|
|
12
|
+
super(options);
|
|
13
|
+
this.#mongoObjectStorage = new MongoObjectStorage({
|
|
14
|
+
viewModelMongoDb: options.viewModelMongoDb,
|
|
15
|
+
viewModelMongoDbFactory: options.viewModelMongoDbFactory,
|
|
16
|
+
tableName: `${options.tableNamePrefix}_${options.schemaVersion}`
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
async get(id) {
|
|
20
|
+
if (!this.ready)
|
|
21
|
+
await this.once('ready');
|
|
22
|
+
return this.#mongoObjectStorage.get(id);
|
|
23
|
+
}
|
|
24
|
+
async create(id, data) {
|
|
25
|
+
await this.#mongoObjectStorage.create(id, data);
|
|
26
|
+
}
|
|
27
|
+
async update(id, update) {
|
|
28
|
+
await this.#mongoObjectStorage.update(id, update);
|
|
29
|
+
}
|
|
30
|
+
async updateEnforcingNew(id, update) {
|
|
31
|
+
await this.#mongoObjectStorage.updateEnforcingNew(id, update);
|
|
32
|
+
}
|
|
33
|
+
async delete(id) {
|
|
34
|
+
return this.#mongoObjectStorage.delete(id);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=MongoObjectView.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MongoObjectView.js","sourceRoot":"","sources":["../../../src/mongodb/MongoObjectView.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD;;GAEG;AACH,MAAM,OAAO,eAAyB,SAAQ,iBAAiB;IAErD,mBAAmB,CAA8B;IAE1D,YAAY,OAEX;QACA,YAAY,CAAC,OAAO,EAAE,eAAe,EAAE,iBAAiB,CAAC,CAAC;QAC1D,YAAY,CAAC,OAAO,EAAE,aAAa,EAAE,eAAe,CAAC,CAAC;QAEtD,KAAK,CAAC,OAAO,CAAC,CAAC;QAEf,IAAI,CAAC,mBAAmB,GAAG,IAAI,kBAAkB,CAAU;YAC1D,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;YAC1C,uBAAuB,EAAE,OAAO,CAAC,uBAAuB;YACxD,SAAS,EAAE,GAAG,OAAO,CAAC,eAAe,IAAI,OAAO,CAAC,aAAa,EAAE;SAChE,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,EAAc;QACvB,IAAI,CAAC,IAAI,CAAC,KAAK;YACd,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAE1B,OAAO,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACzC,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAc,EAAE,IAAa;QACzC,MAAM,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IACjD,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAc,EAAE,MAA+B;QAC3D,MAAM,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IACnD,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,EAAc,EAAE,MAAgC;QACxE,MAAM,IAAI,CAAC,mBAAmB,CAAC,kBAAkB,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IAC/D,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAc;QAC1B,OAAO,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC5C,CAAC;CACD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MongoProjectionDataParams.js","sourceRoot":"","sources":["../../../src/mongodb/MongoProjectionDataParams.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { assertString, Deferred } from "../utils/index.js";
|
|
2
|
+
import { promisify } from 'util';
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
4
|
+
import { AbstractMongoAccessor } from "./AbstractMongoAccessor.js";
|
|
5
|
+
const delay = promisify(setTimeout);
|
|
6
|
+
/**
|
|
7
|
+
* MongoDB-backed implementation of IViewLocker.
|
|
8
|
+
*
|
|
9
|
+
* Uses a MongoDB document with token + lockedTill semantics to acquire a distributed view lock.
|
|
10
|
+
* The lock is automatically prolonged at half the TTL interval to prevent expiration
|
|
11
|
+
* while processing is in progress.
|
|
12
|
+
*
|
|
13
|
+
* Collection name: `ncqrs_view_locks`
|
|
14
|
+
*/
|
|
15
|
+
export class MongoViewLocker extends AbstractMongoAccessor {
|
|
16
|
+
static DEFAULT_VIEW_LOCK_TTL = 120_000;
|
|
17
|
+
static DEFAULT_COLLECTION = 'ncqrs_view_locks';
|
|
18
|
+
#projectionName;
|
|
19
|
+
#lockId;
|
|
20
|
+
#viewLockTtl;
|
|
21
|
+
#collectionName;
|
|
22
|
+
#logger;
|
|
23
|
+
#lockToken;
|
|
24
|
+
#lockMarker;
|
|
25
|
+
#lockProlongationTimeout;
|
|
26
|
+
#collection;
|
|
27
|
+
constructor(o) {
|
|
28
|
+
super(o);
|
|
29
|
+
assertString(o.projectionName, 'projectionName');
|
|
30
|
+
assertString(o.schemaVersion, 'schemaVersion');
|
|
31
|
+
if (o.viewLocksCollection !== undefined)
|
|
32
|
+
assertString(o.viewLocksCollection, 'viewLocksCollection');
|
|
33
|
+
this.#projectionName = o.projectionName;
|
|
34
|
+
this.#collectionName = o.viewLocksCollection ?? MongoViewLocker.DEFAULT_COLLECTION;
|
|
35
|
+
this.#lockId = `${o.projectionName}:${o.schemaVersion}`;
|
|
36
|
+
this.#viewLockTtl = o.viewLockTtl ?? MongoViewLocker.DEFAULT_VIEW_LOCK_TTL;
|
|
37
|
+
this.#logger = o.logger && 'child' in o.logger ?
|
|
38
|
+
o.logger.child({ service: this.constructor.name }) :
|
|
39
|
+
o.logger;
|
|
40
|
+
}
|
|
41
|
+
async initialize(db) {
|
|
42
|
+
this.#collection = db.collection(this.#collectionName);
|
|
43
|
+
await this.#collection.createIndex({ lockedTill: 1 }, { sparse: true });
|
|
44
|
+
}
|
|
45
|
+
get ready() {
|
|
46
|
+
return !this.#lockMarker;
|
|
47
|
+
}
|
|
48
|
+
async lock() {
|
|
49
|
+
this.#lockMarker = new Deferred();
|
|
50
|
+
this.#lockToken = randomUUID();
|
|
51
|
+
await this.assertConnection();
|
|
52
|
+
let lockAcquired = false;
|
|
53
|
+
while (!lockAcquired) {
|
|
54
|
+
const now = new Date();
|
|
55
|
+
const lockedTill = new Date(now.getTime() + this.#viewLockTtl);
|
|
56
|
+
// Claim an expired or released lock if one exists
|
|
57
|
+
const updateResult = await this.#collection.updateOne({
|
|
58
|
+
_id: this.#lockId,
|
|
59
|
+
$or: [
|
|
60
|
+
{ lockedTill: null },
|
|
61
|
+
{ lockedTill: { $exists: false } },
|
|
62
|
+
{ lockedTill: { $lt: now } }
|
|
63
|
+
]
|
|
64
|
+
}, { $set: { lockedTill, token: this.#lockToken } });
|
|
65
|
+
if (updateResult.modifiedCount === 1) {
|
|
66
|
+
lockAcquired = true;
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
// No existing document matched — try to insert a fresh lock
|
|
70
|
+
try {
|
|
71
|
+
await this.#collection.insertOne({
|
|
72
|
+
_id: this.#lockId,
|
|
73
|
+
lockedTill,
|
|
74
|
+
token: this.#lockToken,
|
|
75
|
+
lastEvent: null
|
|
76
|
+
});
|
|
77
|
+
lockAcquired = true;
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
if (typeof err === 'object' && err !== null && 'code' in err && err.code === 11000) {
|
|
81
|
+
// Document exists and is actively locked — wait and retry
|
|
82
|
+
this.#logger?.debug(`"${this.#projectionName}" is locked by another process`);
|
|
83
|
+
await delay(this.#viewLockTtl / 2);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
throw err;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
this.#logger?.debug(`"${this.#projectionName}" lock obtained for ${this.#viewLockTtl}ms`);
|
|
92
|
+
this.scheduleLockProlongation();
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
scheduleLockProlongation() {
|
|
96
|
+
const ms = this.#viewLockTtl / 2;
|
|
97
|
+
this.#lockProlongationTimeout = setTimeout(() => this.prolongLock(), ms);
|
|
98
|
+
this.#lockProlongationTimeout.unref();
|
|
99
|
+
this.#logger?.debug(`"${this.#projectionName}" lock refresh scheduled in ${ms}ms`);
|
|
100
|
+
}
|
|
101
|
+
cancelLockProlongation() {
|
|
102
|
+
clearTimeout(this.#lockProlongationTimeout);
|
|
103
|
+
this.#logger?.debug(`"${this.#projectionName}" lock refresh canceled`);
|
|
104
|
+
}
|
|
105
|
+
async prolongLock() {
|
|
106
|
+
await this.assertConnection();
|
|
107
|
+
const lockedTill = new Date(Date.now() + this.#viewLockTtl);
|
|
108
|
+
const result = await this.#collection.findOneAndUpdate({ _id: this.#lockId, token: this.#lockToken }, { $set: { lockedTill } });
|
|
109
|
+
if (!result)
|
|
110
|
+
throw new Error(`"${this.#projectionName}" lock could not be prolonged`);
|
|
111
|
+
this.#logger?.debug(`"${this.#projectionName}" lock prolonged for ${this.#viewLockTtl}ms`);
|
|
112
|
+
this.scheduleLockProlongation();
|
|
113
|
+
}
|
|
114
|
+
async unlock() {
|
|
115
|
+
this.#lockMarker?.resolve();
|
|
116
|
+
this.#lockMarker = undefined;
|
|
117
|
+
this.cancelLockProlongation();
|
|
118
|
+
await this.assertConnection();
|
|
119
|
+
const result = await this.#collection.findOneAndUpdate({ _id: this.#lockId, token: this.#lockToken }, { $set: { lockedTill: null, token: null } });
|
|
120
|
+
this.#lockToken = undefined;
|
|
121
|
+
if (result)
|
|
122
|
+
this.#logger?.debug(`"${this.#projectionName}" lock released`);
|
|
123
|
+
else
|
|
124
|
+
this.#logger?.warn(`"${this.#projectionName}" lock didn't exist`);
|
|
125
|
+
}
|
|
126
|
+
once(event) {
|
|
127
|
+
if (event !== 'ready')
|
|
128
|
+
throw new TypeError(`Unexpected event: ${event}`);
|
|
129
|
+
return this.#lockMarker?.promise ?? Promise.resolve();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=MongoViewLocker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MongoViewLocker.js","sourceRoot":"","sources":["../../../src/mongodb/MongoViewLocker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import "./IContainer.js";
|
|
2
|
+
export * from "./AbstractMongoAccessor.js";
|
|
3
|
+
export * from "./AbstractMongoObjectProjection.js";
|
|
4
|
+
export * from "./AbstractMongoView.js";
|
|
5
|
+
export * from "./MongoEventLocker.js";
|
|
6
|
+
export * from "./MongoEventStorage.js";
|
|
7
|
+
export * from "./MongoObjectStorage.js";
|
|
8
|
+
export * from "./MongoObjectView.js";
|
|
9
|
+
export * from "./MongoProjectionDataParams.js";
|
|
10
|
+
export * from "./MongoViewLocker.js";
|
|
11
|
+
export * from "./utils/index.js";
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/mongodb/index.ts"],"names":[],"mappings":"AAAA,OAAO,iBAAiB,CAAC;AAEzB,cAAc,4BAA4B,CAAC;AAC3C,cAAc,oCAAoC,CAAC;AACnD,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registers cleanup handlers for SIGINT and SIGTERM signals on a Node.js process.
|
|
3
|
+
* Executes the provided cleanup procedure when one of these signals is received,
|
|
4
|
+
* then removes the listeners to allow the process to exit gracefully.
|
|
5
|
+
*
|
|
6
|
+
* @returns An object with a `dispose` method to manually remove the registered signal handlers.
|
|
7
|
+
*/
|
|
8
|
+
export const registerExitCleanup = (process, cleanupProcedure) => {
|
|
9
|
+
const handler = async () => {
|
|
10
|
+
// remove listeners to allow the process to exit
|
|
11
|
+
process?.off('SIGINT', handler);
|
|
12
|
+
process?.off('SIGTERM', handler);
|
|
13
|
+
await cleanupProcedure();
|
|
14
|
+
};
|
|
15
|
+
process?.once('SIGINT', handler);
|
|
16
|
+
process?.once('SIGTERM', handler);
|
|
17
|
+
return {
|
|
18
|
+
dispose: () => {
|
|
19
|
+
process?.off('SIGINT', handler);
|
|
20
|
+
process?.off('SIGTERM', handler);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=registerExitCleanup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registerExitCleanup.js","sourceRoot":"","sources":["../../../src/mongodb/registerExitCleanup.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAClC,OAAmC,EACnC,gBAAkD,EACjD,EAAE;IACH,MAAM,OAAO,GAAG,KAAK,IAAI,EAAE;QAC1B,gDAAgD;QAChD,OAAO,EAAE,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAChC,OAAO,EAAE,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAEjC,MAAM,gBAAgB,EAAE,CAAC;IAC1B,CAAC,CAAC;IAEF,OAAO,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACjC,OAAO,EAAE,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAElC,OAAO;QACN,OAAO,EAAE,GAAG,EAAE;YACb,OAAO,EAAE,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAChC,OAAO,EAAE,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAClC,CAAC;KACD,CAAC;AACH,CAAC,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
/**
|
|
3
|
+
* Get assigned or generate a deterministic event ID as a hex string
|
|
4
|
+
*/
|
|
5
|
+
export const getEventId = (event) => {
|
|
6
|
+
if (typeof event.id === 'string')
|
|
7
|
+
return event.id;
|
|
8
|
+
return createHash('md5').update(JSON.stringify(event)).digest('hex');
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=getEventId.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getEventId.js","sourceRoot":"","sources":["../../../../src/mongodb/utils/getEventId.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzC;;GAEG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,KAAa,EAAU,EAAE;IACnD,IAAI,OAAO,KAAK,CAAC,EAAE,KAAK,QAAQ;QAC/B,OAAO,KAAK,CAAC,EAAE,CAAC;IAEjB,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACtE,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/mongodb/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC"}
|
|
@@ -3,7 +3,9 @@ import { resolveProvider } from "./utils/index.js";
|
|
|
3
3
|
async function resolveConfig(provider) {
|
|
4
4
|
const {
|
|
5
5
|
// eslint-disable-next-line no-use-before-define
|
|
6
|
-
exchange = RabbitMqCommandBus.DEFAULT_EXCHANGE,
|
|
6
|
+
exchange = RabbitMqCommandBus.DEFAULT_EXCHANGE,
|
|
7
|
+
// eslint-disable-next-line no-use-before-define
|
|
8
|
+
queueName = RabbitMqCommandBus.DEFAULT_QUEUE_NAME, ignoreOwn = false, concurrentLimit, handlerProcessTimeout, queueExpires } = await resolveProvider(provider) ?? {};
|
|
7
9
|
assertString(exchange, 'rabbitMqCommandConfig.exchange');
|
|
8
10
|
assertString(queueName, 'rabbitMqCommandConfig.queueName');
|
|
9
11
|
assertBoolean(ignoreOwn, 'rabbitMqCommandConfig.ignoreOwn');
|
|
@@ -23,6 +25,7 @@ async function resolveConfig(provider) {
|
|
|
23
25
|
*/
|
|
24
26
|
export class RabbitMqCommandBus {
|
|
25
27
|
static DEFAULT_EXCHANGE = 'node-cqrs.commands';
|
|
28
|
+
static DEFAULT_QUEUE_NAME = 'node-cqrs.commands.default';
|
|
26
29
|
#gateway;
|
|
27
30
|
#configProvider;
|
|
28
31
|
#config;
|
|
@@ -35,17 +38,27 @@ export class RabbitMqCommandBus {
|
|
|
35
38
|
this.#config ??= await resolveConfig(this.#configProvider);
|
|
36
39
|
return this.#config;
|
|
37
40
|
}
|
|
38
|
-
async send(commandOrType,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
async send(commandOrType, aggregateIdOrMeta, options) {
|
|
42
|
+
let command;
|
|
43
|
+
let meta;
|
|
44
|
+
if (typeof commandOrType === 'string') {
|
|
45
|
+
const { span, ...commandOptions } = options ?? {};
|
|
46
|
+
command = { type: commandOrType, aggregateId: aggregateIdOrMeta, ...commandOptions };
|
|
47
|
+
if (span)
|
|
48
|
+
meta = { span };
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
command = commandOrType;
|
|
52
|
+
if (aggregateIdOrMeta && typeof aggregateIdOrMeta === 'object')
|
|
53
|
+
meta = aggregateIdOrMeta;
|
|
54
|
+
}
|
|
42
55
|
assertMessage(command, 'command');
|
|
43
56
|
const { exchange } = await this.#resolveConfig();
|
|
44
|
-
await this.#gateway.publish(exchange, command);
|
|
57
|
+
await this.#gateway.publish(exchange, command, meta);
|
|
45
58
|
}
|
|
46
59
|
/** @deprecated Use {@link send} */
|
|
47
|
-
sendRaw(command) {
|
|
48
|
-
return this.send(command);
|
|
60
|
+
sendRaw(command, meta) {
|
|
61
|
+
return this.send(command, meta);
|
|
49
62
|
}
|
|
50
63
|
/**
|
|
51
64
|
* Registers a message handler for a specific message type on the durable queue.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RabbitMqCommandBus.js","sourceRoot":"","sources":["../../../src/rabbitmq/RabbitMqCommandBus.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,wBAAwB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAExH,OAAO,EAAuB,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAQxE,KAAK,UAAU,aAAa,CAAC,QAAmD;IAC/E,MAAM;IACL,gDAAgD;IAChD,QAAQ,GAAG,kBAAkB,CAAC,gBAAgB,
|
|
1
|
+
{"version":3,"file":"RabbitMqCommandBus.js","sourceRoot":"","sources":["../../../src/rabbitmq/RabbitMqCommandBus.ts"],"names":[],"mappings":"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"}
|
|
@@ -51,9 +51,9 @@ export class RabbitMqEventBus {
|
|
|
51
51
|
* Publishes a message to the event exchange.
|
|
52
52
|
* The message will be delivered to all subscribers.
|
|
53
53
|
*/
|
|
54
|
-
async publish(message) {
|
|
54
|
+
async publish(message, meta) {
|
|
55
55
|
const { exchange } = await this.#resolveConfig();
|
|
56
|
-
await this.#gateway.publish(exchange, message);
|
|
56
|
+
await this.#gateway.publish(exchange, message, meta);
|
|
57
57
|
}
|
|
58
58
|
/**
|
|
59
59
|
* Registers a message handler for a specific message type.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RabbitMqEventBus.js","sourceRoot":"","sources":["../../../src/rabbitmq/RabbitMqEventBus.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,wBAAwB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACzG,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,eAAe,EAA2C,MAAM,sBAAsB,CAAC;AAChG,OAAO,EAAuB,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAQxE,KAAK,UAAU,aAAa,CAAC,QAAiD;IAC7E,MAAM;IACL,gDAAgD;IAChD,QAAQ,GAAG,gBAAgB,CAAC,gBAAgB,EAC5C,SAAS,GAAG,IAAI,EAChB,eAAe,EACf,qBAAqB,EACrB,SAAS,EACT,YAAY,EACZ,GAAG,MAAM,eAAe,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;IAE1C,YAAY,CAAC,QAAQ,EAAE,8BAA8B,CAAC,CAAC;IACvD,aAAa,CAAC,SAAS,EAAE,+BAA+B,CAAC,CAAC;IAC1D,IAAI,eAAe,KAAK,SAAS;QAChC,wBAAwB,CAAC,eAAe,EAAE,qCAAqC,CAAC,CAAC;IAClF,IAAI,qBAAqB,KAAK,SAAS;QACtC,wBAAwB,CAAC,qBAAqB,EAAE,2CAA2C,CAAC,CAAC;IAC9F,IAAI,SAAS,KAAK,SAAS;QAC1B,YAAY,CAAC,SAAS,EAAE,+BAA+B,CAAC,CAAC;IAC1D,IAAI,YAAY,KAAK,SAAS;QAC7B,wBAAwB,CAAC,YAAY,EAAE,kCAAkC,CAAC,CAAC;IAE5E,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,eAAe,EAAE,qBAAqB,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC;AACjG,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,OAAO,gBAAgB;IAE5B,MAAM,KAAK,iBAAiB;QAC3B,OAAO,eAAe,CAAC,mBAAmB,CAAC;IAC5C,CAAC;IAED,MAAM,CAAC,gBAAgB,GAAG,kBAAkB,CAAC;IAEpC,QAAQ,CAAkB;IAC1B,OAAO,GAAG,IAAI,GAAG,EAA8B,CAAC;IAChD,eAAe,CAA0C;IAClE,OAAO,CAAkC;IAEzC,YAAY,EACX,eAAe,EACf,sBAAsB,EAC0C;QAChE,aAAa,CAAC,eAAe,EAAE,iBAAiB,CAAC,CAAC;QAElD,IAAI,CAAC,QAAQ,GAAG,eAAe,CAAC;QAChC,IAAI,CAAC,eAAe,GAAG,sBAAsB,CAAC;IAC/C,CAAC;IAED,KAAK,CAAC,cAAc;QACnB,IAAI,CAAC,OAAO,KAAK,MAAM,aAAa,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAC3D,OAAO,IAAI,CAAC,OAAO,CAAC;IACrB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,OAAO,CAAC,OAAiB;
|
|
1
|
+
{"version":3,"file":"RabbitMqEventBus.js","sourceRoot":"","sources":["../../../src/rabbitmq/RabbitMqEventBus.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
var _a;
|
|
2
|
+
import { propagation, context } from '@opentelemetry/api';
|
|
2
3
|
import * as Event from "../Event.js";
|
|
4
|
+
import { recordSpanError, spanAttributes, spanContext } from "../telemetry/index.js";
|
|
3
5
|
import { assertDefined, assertFunction, assertMessage, assertNonNegativeInteger, assertNotDefined, assertString, extractErrorDetails, Lock } from "../utils/index.js";
|
|
4
6
|
import { registerExitCleanup, resolveProvider } from "./utils/index.js";
|
|
5
7
|
import { EventEmitter } from 'events';
|
|
@@ -31,6 +33,7 @@ export class RabbitMqGateway {
|
|
|
31
33
|
#appId;
|
|
32
34
|
#logger;
|
|
33
35
|
#emitter;
|
|
36
|
+
#tracer;
|
|
34
37
|
#desiredState = 'disconnected';
|
|
35
38
|
#stateChangeLock = new Lock();
|
|
36
39
|
/**
|
|
@@ -49,12 +52,13 @@ export class RabbitMqGateway {
|
|
|
49
52
|
isConnected() {
|
|
50
53
|
return !!this.#connection;
|
|
51
54
|
}
|
|
52
|
-
constructor({ rabbitMqConnectionFactory, rabbitMqAppId = getRandomAppId(), eventEmitterFactory, logger, process }) {
|
|
55
|
+
constructor({ rabbitMqConnectionFactory, rabbitMqAppId = getRandomAppId(), eventEmitterFactory, logger, process, tracerFactory }) {
|
|
53
56
|
assertFunction(rabbitMqConnectionFactory, 'rabbitMqConnectionFactory');
|
|
54
57
|
assertDefined(rabbitMqAppId, 'rabbitMqAppId');
|
|
55
58
|
this.#emitter = eventEmitterFactory?.() ?? new EventEmitter();
|
|
56
59
|
this.#connectionFactory = rabbitMqConnectionFactory;
|
|
57
60
|
this.#appIdProvider = rabbitMqAppId;
|
|
61
|
+
this.#tracer = tracerFactory?.(new.target.name);
|
|
58
62
|
this.#logger = logger && 'child' in logger ?
|
|
59
63
|
logger.child({ service: new.target.name }) :
|
|
60
64
|
logger;
|
|
@@ -398,13 +402,16 @@ export class RabbitMqGateway {
|
|
|
398
402
|
messageFinalized = true;
|
|
399
403
|
}, handlerProcessTimeout)
|
|
400
404
|
: null;
|
|
405
|
+
this.#logger?.debug('Message received', {
|
|
406
|
+
queueName: queueGivenName,
|
|
407
|
+
msg: extractMessageMeta(msg)
|
|
408
|
+
});
|
|
409
|
+
const jsonContent = msg.content.toString();
|
|
410
|
+
const message = JSON.parse(jsonContent);
|
|
411
|
+
// Extract OTel trace context from AMQP headers to continue the distributed trace
|
|
412
|
+
const parentContext = propagation.extract(context.active(), msg.properties?.headers ?? {});
|
|
413
|
+
const span = this.#tracer?.startSpan(`RabbitMqGateway.consume ${message.type}`, spanAttributes('rabbitmq', message, ['type', 'aggregateId']), parentContext);
|
|
401
414
|
try {
|
|
402
|
-
this.#logger?.debug('Message received', {
|
|
403
|
-
queueName: queueGivenName,
|
|
404
|
-
msg: extractMessageMeta(msg)
|
|
405
|
-
});
|
|
406
|
-
const jsonContent = msg.content.toString();
|
|
407
|
-
const message = JSON.parse(jsonContent);
|
|
408
415
|
const handlers = this.#getHandlers(queueGivenName, message.type);
|
|
409
416
|
if (!handlers.length && !isSystemQueue(queueGivenName))
|
|
410
417
|
throw new Error(`Message from queue "${queueGivenName}" was delivered to a consumer that does not handle type "${message.type}"`);
|
|
@@ -412,7 +419,7 @@ export class RabbitMqGateway {
|
|
|
412
419
|
for (const { handler, ignoreOwn } of handlers) {
|
|
413
420
|
if (ignoreOwn && msg.properties.appId === ownAppId)
|
|
414
421
|
continue;
|
|
415
|
-
await handler(message);
|
|
422
|
+
await handler(message, { span });
|
|
416
423
|
}
|
|
417
424
|
if (messageFinalized) {
|
|
418
425
|
this.#logger?.error('Handler resolved, but message has already been finalized', {
|
|
@@ -430,6 +437,7 @@ export class RabbitMqGateway {
|
|
|
430
437
|
queueName: queueGivenName,
|
|
431
438
|
msg: extractMessageMeta(msg)
|
|
432
439
|
});
|
|
440
|
+
recordSpanError(span, err);
|
|
433
441
|
if (!messageFinalized) {
|
|
434
442
|
this.#rejectMessage(channel, msg);
|
|
435
443
|
messageFinalized = true;
|
|
@@ -438,6 +446,7 @@ export class RabbitMqGateway {
|
|
|
438
446
|
finally {
|
|
439
447
|
if (keepAliveTimeout !== null)
|
|
440
448
|
clearTimeout(keepAliveTimeout);
|
|
449
|
+
span?.end();
|
|
441
450
|
}
|
|
442
451
|
}, {
|
|
443
452
|
noAck: options?.noAck
|
|
@@ -484,45 +493,61 @@ export class RabbitMqGateway {
|
|
|
484
493
|
* Publishes an event to the fanout exchange.
|
|
485
494
|
* The event will be delivered to all subscribers, except this instance's own consumer.
|
|
486
495
|
*/
|
|
487
|
-
async publish(exchange, message) {
|
|
496
|
+
async publish(exchange, message, meta) {
|
|
488
497
|
assertString(exchange, 'exchange');
|
|
489
498
|
assertMessage(message, 'message');
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
this.#pubChannel
|
|
493
|
-
|
|
499
|
+
const span = this.#tracer?.startSpan(`RabbitMqGateway.publish ${message.type}`, spanAttributes('rabbitmq', message, ['type', 'aggregateId']), spanContext(meta));
|
|
500
|
+
try {
|
|
501
|
+
if (!this.#pubChannel) {
|
|
502
|
+
const connection = await this.#assertConnection();
|
|
503
|
+
this.#pubChannel = await connection.createConfirmChannel();
|
|
504
|
+
await this.#pubChannel.assertExchange(exchange, 'topic', { durable: true });
|
|
505
|
+
}
|
|
506
|
+
const content = Buffer.from(JSON.stringify(message), 'utf8');
|
|
507
|
+
const sagaCorrelationId = (() => {
|
|
508
|
+
const sagaOrigins = message.sagaOrigins;
|
|
509
|
+
if (!sagaOrigins || typeof sagaOrigins !== 'object')
|
|
510
|
+
return undefined;
|
|
511
|
+
const sagaDescriptors = Object.keys(sagaOrigins);
|
|
512
|
+
if (sagaDescriptors.length !== 1)
|
|
513
|
+
return undefined;
|
|
514
|
+
return sagaOrigins[sagaDescriptors[0]];
|
|
515
|
+
})();
|
|
516
|
+
// Inject OTel trace context into AMQP headers so consumers can continue the trace
|
|
517
|
+
const headers = {};
|
|
518
|
+
const parentContext = span ?
|
|
519
|
+
spanContext({ span }) :
|
|
520
|
+
spanContext(meta);
|
|
521
|
+
propagation.inject(parentContext, headers);
|
|
522
|
+
const properties = {
|
|
523
|
+
contentType: 'application/json',
|
|
524
|
+
contentEncoding: 'utf8',
|
|
525
|
+
persistent: true,
|
|
526
|
+
timestamp: message.context?.ts ?? Date.now(),
|
|
527
|
+
appId: await this.getAppId(),
|
|
528
|
+
type: message.type,
|
|
529
|
+
messageId: 'id' in message && typeof message.id === 'string' ?
|
|
530
|
+
message.id :
|
|
531
|
+
undefined,
|
|
532
|
+
correlationId: sagaCorrelationId,
|
|
533
|
+
headers
|
|
534
|
+
};
|
|
535
|
+
return await new Promise((resolve, reject) => {
|
|
536
|
+
if (!this.#pubChannel)
|
|
537
|
+
throw new Error('No channel available for publishing');
|
|
538
|
+
this.#logger?.debug(`Publishing message "${Event.describe(message)}" to exchange "${exchange}"`);
|
|
539
|
+
const published = this.#pubChannel.publish(exchange, message.type, content, properties, err => (err ? reject(err) : resolve()));
|
|
540
|
+
if (!published)
|
|
541
|
+
throw new Error(`Failed to send event ${Event.describe(message)}, channel buffer is full`);
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
catch (error) {
|
|
545
|
+
recordSpanError(span, error);
|
|
546
|
+
throw error;
|
|
547
|
+
}
|
|
548
|
+
finally {
|
|
549
|
+
span?.end();
|
|
494
550
|
}
|
|
495
|
-
const content = Buffer.from(JSON.stringify(message), 'utf8');
|
|
496
|
-
const sagaCorrelationId = (() => {
|
|
497
|
-
const sagaOrigins = message.sagaOrigins;
|
|
498
|
-
if (!sagaOrigins || typeof sagaOrigins !== 'object')
|
|
499
|
-
return undefined;
|
|
500
|
-
const sagaDescriptors = Object.keys(sagaOrigins);
|
|
501
|
-
if (sagaDescriptors.length !== 1)
|
|
502
|
-
return undefined;
|
|
503
|
-
return sagaOrigins[sagaDescriptors[0]];
|
|
504
|
-
})();
|
|
505
|
-
const appId = await this.getAppId();
|
|
506
|
-
const properties = {
|
|
507
|
-
contentType: 'application/json',
|
|
508
|
-
contentEncoding: 'utf8',
|
|
509
|
-
persistent: true,
|
|
510
|
-
timestamp: message.context?.ts ?? Date.now(),
|
|
511
|
-
appId,
|
|
512
|
-
type: message.type,
|
|
513
|
-
messageId: 'id' in message && typeof message.id === 'string' ?
|
|
514
|
-
message.id :
|
|
515
|
-
undefined,
|
|
516
|
-
correlationId: sagaCorrelationId
|
|
517
|
-
};
|
|
518
|
-
return new Promise((resolve, reject) => {
|
|
519
|
-
if (!this.#pubChannel)
|
|
520
|
-
throw new Error('No channel available for publishing');
|
|
521
|
-
this.#logger?.debug(`Publishing message "${Event.describe(message)}" to exchange "${exchange}"`);
|
|
522
|
-
const published = this.#pubChannel.publish(exchange, message.type, content, properties, err => (err ? reject(err) : resolve()));
|
|
523
|
-
if (!published)
|
|
524
|
-
throw new Error(`Failed to send event ${Event.describe(message)}, channel buffer is full`);
|
|
525
|
-
});
|
|
526
551
|
}
|
|
527
552
|
on(event, fn) {
|
|
528
553
|
this.#emitter.on(event, fn);
|