@event-driven-io/emmett-mongodb 0.21.1 → 0.22.0-alpha.2
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/dist/index.cjs +113 -77
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +40 -28
- package/dist/index.d.ts +40 -28
- package/dist/index.js +113 -77
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,47 +1,59 @@
|
|
|
1
|
-
import { Event, ReadEvent,
|
|
2
|
-
import { Collection } from 'mongodb';
|
|
1
|
+
import { Event, EventMetaDataOf, ReadEvent, ReadEventMetadataWithoutGlobalPosition, ProjectionRegistration, EventStore, ReadStreamOptions, ReadStreamResult, AggregateStreamOptions, AggregateStreamResult, AppendToStreamOptions, AppendToStreamResult } from '@event-driven-io/emmett';
|
|
2
|
+
import { UpdateFilter, Collection, Document } from 'mongodb';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
type MongoDBProjectionInlineHandlerContext<EventType extends Event = Event, EventMetaDataType extends EventMetaDataOf<EventType> & MongoDBReadEventMetadata = EventMetaDataOf<EventType> & MongoDBReadEventMetadata> = {
|
|
5
|
+
document: MongoDBReadModel | null;
|
|
6
|
+
updates: UpdateFilter<EventStream<EventType, EventMetaDataType>>;
|
|
7
|
+
collection: Collection<EventStream<EventType, EventMetaDataType>>;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
declare const MongoDBEventStoreDefaultStreamVersion = 0n;
|
|
5
11
|
type StreamType = string;
|
|
6
12
|
type StreamName<T extends StreamType = StreamType> = `${T}:${string}`;
|
|
7
13
|
type StreamNameParts<T extends StreamType = StreamType> = {
|
|
8
14
|
streamType: T;
|
|
9
15
|
streamId: string;
|
|
10
16
|
};
|
|
11
|
-
type
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
streamVersion: number;
|
|
16
|
-
events: ReadEvent<EventType, ReadEventMetadata>[];
|
|
17
|
+
type MongoDBReadModelMetadata = {
|
|
18
|
+
name: string;
|
|
19
|
+
schemaVersion: number;
|
|
20
|
+
streamPosition: bigint;
|
|
17
21
|
};
|
|
18
|
-
type
|
|
19
|
-
|
|
22
|
+
type MongoDBReadModel<Doc extends Document = Document> = Doc & {
|
|
23
|
+
_metadata: MongoDBReadModelMetadata;
|
|
24
|
+
};
|
|
25
|
+
interface EventStream<EventType extends Event = Event, EventMetaDataType extends EventMetaDataOf<EventType> & MongoDBReadEventMetadata = EventMetaDataOf<EventType> & MongoDBReadEventMetadata> {
|
|
20
26
|
streamName: string;
|
|
21
|
-
events: Array<ReadEvent<EventType,
|
|
22
|
-
|
|
23
|
-
|
|
27
|
+
events: Array<ReadEvent<EventType, EventMetaDataType>>;
|
|
28
|
+
metadata: {
|
|
29
|
+
streamId: string;
|
|
30
|
+
streamType: StreamType;
|
|
31
|
+
streamPosition: bigint;
|
|
32
|
+
createdAt: Date;
|
|
33
|
+
updatedAt: Date;
|
|
34
|
+
};
|
|
35
|
+
projections: Record<string, MongoDBReadModel>;
|
|
24
36
|
}
|
|
25
|
-
type EventStreamEvent<EventType extends Event = Event> = EventStream<EventType>['events'][number];
|
|
26
37
|
interface MongoDBConnectionOptions {
|
|
27
38
|
connectionString: string;
|
|
28
39
|
database: string;
|
|
29
40
|
collection?: string;
|
|
30
41
|
}
|
|
31
|
-
|
|
42
|
+
type MongoDBReadEventMetadata = ReadEventMetadataWithoutGlobalPosition<bigint>;
|
|
43
|
+
type MongoDBReadEvent<EventType extends Event = Event> = ReadEvent<EventType, MongoDBReadEventMetadata>;
|
|
44
|
+
type MongoDBEventStoreOptions = {
|
|
45
|
+
collection: Collection<EventStream>;
|
|
46
|
+
projections?: ProjectionRegistration<'inline', MongoDBReadEventMetadata, MongoDBProjectionInlineHandlerContext>[];
|
|
47
|
+
};
|
|
48
|
+
declare class MongoDBEventStore implements EventStore<MongoDBReadEventMetadata> {
|
|
32
49
|
private readonly collection;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
* These will be ran after a the events have been successfully appended to
|
|
39
|
-
* the stream. `appendToStream` will return after every projection is completed.
|
|
40
|
-
*/
|
|
41
|
-
projections?: Array<Projection<EventType>>;
|
|
42
|
-
}): Promise<AppendToStreamResult<number>>;
|
|
50
|
+
private readonly inlineProjections;
|
|
51
|
+
constructor(options: MongoDBEventStoreOptions);
|
|
52
|
+
readStream<EventType extends Event>(streamName: StreamName, options?: ReadStreamOptions): Promise<Exclude<ReadStreamResult<EventType, MongoDBReadEventMetadata>, null>>;
|
|
53
|
+
aggregateStream<State, EventType extends Event>(streamName: StreamName, options: AggregateStreamOptions<State, EventType, MongoDBReadEventMetadata>): Promise<AggregateStreamResult<State>>;
|
|
54
|
+
appendToStream<EventType extends Event>(streamName: StreamName, events: EventType[], options?: AppendToStreamOptions): Promise<AppendToStreamResult>;
|
|
43
55
|
}
|
|
44
|
-
declare const getMongoDBEventStore: (
|
|
56
|
+
declare const getMongoDBEventStore: (options: ConstructorParameters<typeof MongoDBEventStore>[0]) => MongoDBEventStore;
|
|
45
57
|
/**
|
|
46
58
|
* Accepts a `streamType` (the type/category of the event stream) and an `streamId`
|
|
47
59
|
* (the individual entity/object or aggregate ID) and combines them to a singular
|
|
@@ -54,4 +66,4 @@ declare function toStreamName<T extends StreamType>(streamType: T, streamId: str
|
|
|
54
66
|
*/
|
|
55
67
|
declare function fromStreamName<T extends StreamType>(streamName: StreamName<T>): StreamNameParts<T>;
|
|
56
68
|
|
|
57
|
-
export { type EventStream, type
|
|
69
|
+
export { type EventStream, type MongoDBConnectionOptions, MongoDBEventStore, MongoDBEventStoreDefaultStreamVersion, type MongoDBEventStoreOptions, type MongoDBReadEvent, type MongoDBReadEventMetadata, type MongoDBReadModel, type MongoDBReadModelMetadata, type StreamName, type StreamNameParts, type StreamType, fromStreamName, getMongoDBEventStore, toStreamName };
|
package/dist/index.js
CHANGED
|
@@ -262,16 +262,61 @@ var { retry: retry2 } = streamTransformations;
|
|
|
262
262
|
// src/eventStore/mongoDBEventStore.ts
|
|
263
263
|
import "mongodb";
|
|
264
264
|
import { v4 as uuid4 } from "uuid";
|
|
265
|
-
|
|
265
|
+
|
|
266
|
+
// src/eventStore/projections/index.ts
|
|
267
|
+
var handleInlineProjections = async (options) => {
|
|
268
|
+
const {
|
|
269
|
+
events,
|
|
270
|
+
projections: allProjections,
|
|
271
|
+
updates: update,
|
|
272
|
+
collection,
|
|
273
|
+
readModels
|
|
274
|
+
} = options;
|
|
275
|
+
const eventTypes = events.map((e) => e.type);
|
|
276
|
+
const projections = allProjections.filter(
|
|
277
|
+
(p) => p.canHandle.some((type) => eventTypes.includes(type))
|
|
278
|
+
);
|
|
279
|
+
for (const projection of projections) {
|
|
280
|
+
await projection.handle(events, {
|
|
281
|
+
document: readModels[projection.name] ?? null,
|
|
282
|
+
collection,
|
|
283
|
+
updates: update
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
// src/eventStore/mongoDBEventStore.ts
|
|
289
|
+
var MongoDBEventStoreDefaultStreamVersion = 0n;
|
|
266
290
|
var MongoDBEventStore = class {
|
|
267
291
|
collection;
|
|
268
|
-
|
|
269
|
-
|
|
292
|
+
inlineProjections;
|
|
293
|
+
constructor(options) {
|
|
294
|
+
this.collection = options.collection;
|
|
295
|
+
this.inlineProjections = (options.projections ?? []).filter(({ type }) => type === "inline").map(
|
|
296
|
+
({ projection }) => projection
|
|
297
|
+
);
|
|
270
298
|
}
|
|
271
299
|
async readStream(streamName, options) {
|
|
272
300
|
const expectedStreamVersion = options?.expectedStreamVersion;
|
|
273
|
-
const
|
|
301
|
+
const filter2 = {
|
|
274
302
|
streamName: { $eq: streamName }
|
|
303
|
+
};
|
|
304
|
+
const eventsSliceArr = [];
|
|
305
|
+
if (options && "from" in options) {
|
|
306
|
+
eventsSliceArr.push(Number(options.from));
|
|
307
|
+
} else {
|
|
308
|
+
eventsSliceArr.push(0);
|
|
309
|
+
}
|
|
310
|
+
if (options && "to" in options) {
|
|
311
|
+
eventsSliceArr.push(Number(options.to));
|
|
312
|
+
}
|
|
313
|
+
const eventsSlice = eventsSliceArr.length > 1 ? { $slice: eventsSliceArr } : 1;
|
|
314
|
+
const stream = await this.collection.findOne(filter2, {
|
|
315
|
+
useBigInt64: true,
|
|
316
|
+
projection: {
|
|
317
|
+
metadata: 1,
|
|
318
|
+
events: eventsSlice
|
|
319
|
+
}
|
|
275
320
|
});
|
|
276
321
|
if (!stream) {
|
|
277
322
|
return {
|
|
@@ -281,13 +326,13 @@ var MongoDBEventStore = class {
|
|
|
281
326
|
};
|
|
282
327
|
}
|
|
283
328
|
assertExpectedVersionMatchesCurrent(
|
|
284
|
-
stream.
|
|
329
|
+
stream.metadata.streamPosition,
|
|
285
330
|
expectedStreamVersion,
|
|
286
331
|
MongoDBEventStoreDefaultStreamVersion
|
|
287
332
|
);
|
|
288
333
|
return {
|
|
289
|
-
events: stream.events
|
|
290
|
-
currentStreamVersion: stream.
|
|
334
|
+
events: stream.events,
|
|
335
|
+
currentStreamVersion: stream.metadata.streamPosition,
|
|
291
336
|
streamExists: true
|
|
292
337
|
};
|
|
293
338
|
}
|
|
@@ -301,100 +346,91 @@ var MongoDBEventStore = class {
|
|
|
301
346
|
};
|
|
302
347
|
}
|
|
303
348
|
async appendToStream(streamName, events, options) {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
349
|
+
const expectedStreamVersion = options?.expectedStreamVersion;
|
|
350
|
+
const stream = await this.collection.findOne(
|
|
351
|
+
{ streamName: { $eq: streamName } },
|
|
352
|
+
{
|
|
353
|
+
useBigInt64: true,
|
|
354
|
+
projection: {
|
|
355
|
+
"metadata.streamPosition": 1,
|
|
356
|
+
projections: 1
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
);
|
|
360
|
+
const currentStreamVersion = stream?.metadata.streamPosition ?? MongoDBEventStoreDefaultStreamVersion;
|
|
361
|
+
assertExpectedVersionMatchesCurrent(
|
|
362
|
+
currentStreamVersion,
|
|
363
|
+
expectedStreamVersion,
|
|
364
|
+
MongoDBEventStoreDefaultStreamVersion
|
|
365
|
+
);
|
|
366
|
+
let streamOffset = currentStreamVersion;
|
|
367
|
+
const eventsToAppend = events.map((event) => {
|
|
368
|
+
const metadata = {
|
|
369
|
+
eventId: uuid4(),
|
|
312
370
|
streamName,
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
});
|
|
317
|
-
stream = await this.collection.findOne({
|
|
318
|
-
_id: result.insertedId
|
|
319
|
-
});
|
|
320
|
-
createdNewStream = true;
|
|
321
|
-
}
|
|
322
|
-
const eventCreateInputs = [];
|
|
323
|
-
for (const event of events) {
|
|
324
|
-
currentStreamPosition++;
|
|
325
|
-
eventCreateInputs.push({
|
|
371
|
+
streamPosition: ++streamOffset
|
|
372
|
+
};
|
|
373
|
+
return {
|
|
326
374
|
type: event.type,
|
|
327
375
|
data: event.data,
|
|
328
376
|
metadata: {
|
|
329
|
-
|
|
330
|
-
eventId: uuid4(),
|
|
331
|
-
streamName,
|
|
332
|
-
streamPosition: BigInt(currentStreamPosition),
|
|
377
|
+
...metadata,
|
|
333
378
|
...event.metadata ?? {}
|
|
334
379
|
}
|
|
380
|
+
};
|
|
381
|
+
});
|
|
382
|
+
const updates = {
|
|
383
|
+
$push: { events: { $each: eventsToAppend } },
|
|
384
|
+
$set: { "metadata.updatedAt": /* @__PURE__ */ new Date() },
|
|
385
|
+
$inc: { "metadata.streamPosition": BigInt(events.length) }
|
|
386
|
+
};
|
|
387
|
+
if (this.inlineProjections) {
|
|
388
|
+
await handleInlineProjections({
|
|
389
|
+
readModels: stream?.projections ?? {},
|
|
390
|
+
events: eventsToAppend,
|
|
391
|
+
projections: this.inlineProjections,
|
|
392
|
+
collection: this.collection,
|
|
393
|
+
updates,
|
|
394
|
+
client: {}
|
|
335
395
|
});
|
|
336
396
|
}
|
|
337
|
-
|
|
338
|
-
assertExpectedVersionMatchesCurrent(
|
|
339
|
-
stream.events.length,
|
|
340
|
-
options?.expectedStreamVersion,
|
|
341
|
-
MongoDBEventStoreDefaultStreamVersion
|
|
342
|
-
);
|
|
343
|
-
const updatedStream = await this.collection.findOneAndUpdate(
|
|
397
|
+
const updatedStream = await this.collection.updateOne(
|
|
344
398
|
{
|
|
345
399
|
streamName: { $eq: streamName },
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
$push: { events: { $each: eventCreateInputs } },
|
|
350
|
-
$set: { updatedAt: /* @__PURE__ */ new Date() }
|
|
400
|
+
"metadata.streamPosition": toExpectedVersion(
|
|
401
|
+
options?.expectedStreamVersion
|
|
402
|
+
)
|
|
351
403
|
},
|
|
352
|
-
|
|
404
|
+
updates,
|
|
405
|
+
{ useBigInt64: true, upsert: true }
|
|
353
406
|
);
|
|
354
407
|
if (!updatedStream) {
|
|
355
|
-
const currentStream = await this.collection.findOne({
|
|
356
|
-
streamName: { $eq: streamName }
|
|
357
|
-
});
|
|
358
408
|
throw new ExpectedVersionConflictError(
|
|
359
|
-
|
|
360
|
-
|
|
409
|
+
currentStreamVersion,
|
|
410
|
+
options?.expectedStreamVersion ?? 0n
|
|
361
411
|
);
|
|
362
412
|
}
|
|
363
|
-
const { streamType, streamId } = fromStreamName(streamName);
|
|
364
|
-
await executeProjections(
|
|
365
|
-
{
|
|
366
|
-
streamName,
|
|
367
|
-
streamType,
|
|
368
|
-
streamId,
|
|
369
|
-
streamVersion: updatedStream.events.length,
|
|
370
|
-
events: updatedStream.events
|
|
371
|
-
},
|
|
372
|
-
options?.projections
|
|
373
|
-
);
|
|
374
413
|
return {
|
|
375
|
-
nextExpectedStreamVersion:
|
|
376
|
-
createdNewStream
|
|
414
|
+
nextExpectedStreamVersion: currentStreamVersion + BigInt(eventsToAppend.length),
|
|
415
|
+
createdNewStream: currentStreamVersion === MongoDBEventStoreDefaultStreamVersion
|
|
377
416
|
};
|
|
378
417
|
}
|
|
379
418
|
};
|
|
380
|
-
var getMongoDBEventStore = (
|
|
381
|
-
const eventStore = new MongoDBEventStore(
|
|
419
|
+
var getMongoDBEventStore = (options) => {
|
|
420
|
+
const eventStore = new MongoDBEventStore(options);
|
|
382
421
|
return eventStore;
|
|
383
422
|
};
|
|
384
|
-
function
|
|
385
|
-
return Promise.all((projections ?? []).map((project) => project(params)));
|
|
386
|
-
}
|
|
387
|
-
function maxEventIndex(expectedStreamVersion) {
|
|
423
|
+
function toExpectedVersion(expectedStreamVersion) {
|
|
388
424
|
if (!expectedStreamVersion) return void 0;
|
|
389
|
-
if (typeof expectedStreamVersion === "
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
return void 0;
|
|
425
|
+
if (typeof expectedStreamVersion === "string") {
|
|
426
|
+
switch (expectedStreamVersion) {
|
|
427
|
+
case STREAM_DOES_NOT_EXIST:
|
|
428
|
+
return BigInt(0);
|
|
429
|
+
default:
|
|
430
|
+
return void 0;
|
|
431
|
+
}
|
|
397
432
|
}
|
|
433
|
+
return expectedStreamVersion;
|
|
398
434
|
}
|
|
399
435
|
function toStreamName(streamType, streamId) {
|
|
400
436
|
return `${streamType}:${streamId}`;
|