@event-driven-io/emmett-mongodb 0.22.0 → 0.23.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/dist/index.d.ts CHANGED
@@ -1,11 +1,45 @@
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';
1
+ import { Event, EventMetaDataOf, ProjectionHandler, TypedProjectionDefinition, ReadEvent, CanHandle, ReadEventMetadataWithoutGlobalPosition, ProjectionRegistration, EventStore } from '@event-driven-io/emmett';
2
+ import { UpdateFilter, Collection, Document, MongoClient, MongoClientOptions } from 'mongodb';
3
3
 
4
4
  type MongoDBProjectionInlineHandlerContext<EventType extends Event = Event, EventMetaDataType extends EventMetaDataOf<EventType> & MongoDBReadEventMetadata = EventMetaDataOf<EventType> & MongoDBReadEventMetadata> = {
5
5
  document: MongoDBReadModel | null;
6
6
  updates: UpdateFilter<EventStream<EventType, EventMetaDataType>>;
7
7
  collection: Collection<EventStream<EventType, EventMetaDataType>>;
8
8
  };
9
+ type MongoDBInlineProjectionHandler<EventType extends Event = Event, EventMetaDataType extends EventMetaDataOf<EventType> & MongoDBReadEventMetadata = EventMetaDataOf<EventType> & MongoDBReadEventMetadata> = ProjectionHandler<EventType, EventMetaDataType, MongoDBProjectionInlineHandlerContext>;
10
+ type MongoDBInlineProjectionDefinition<EventType extends Event = Event, EventMetaDataType extends EventMetaDataOf<EventType> & MongoDBReadEventMetadata = EventMetaDataOf<EventType> & MongoDBReadEventMetadata> = TypedProjectionDefinition<EventType, EventMetaDataType, MongoDBProjectionInlineHandlerContext> & {
11
+ name: string;
12
+ };
13
+ type InlineProjectionHandlerOptions<EventType extends Event = Event, EventMetaDataType extends EventMetaDataOf<EventType> & MongoDBReadEventMetadata = EventMetaDataOf<EventType> & MongoDBReadEventMetadata> = {
14
+ readModels: Record<string, MongoDBReadModel>;
15
+ events: Array<ReadEvent<EventType, EventMetaDataType>>;
16
+ projections: MongoDBInlineProjectionDefinition<EventType, EventMetaDataType>[];
17
+ collection: Collection<EventStream>;
18
+ updates: UpdateFilter<EventStream<Event>>;
19
+ client: {};
20
+ };
21
+ declare const handleInlineProjections: <EventType extends Event = Event, EventMetaDataType extends EventMetaDataOf<EventType> & MongoDBReadEventMetadata = EventMetaDataOf<EventType> & Readonly<{
22
+ eventId: string;
23
+ streamPosition: bigint;
24
+ streamName: string;
25
+ }> & object>(options: InlineProjectionHandlerOptions<EventType, EventMetaDataType>) => Promise<void>;
26
+ type MongoDBWithNotNullDocumentEvolve<Doc extends Document, EventType extends Event, EventMetaDataType extends EventMetaDataOf<EventType> & MongoDBReadEventMetadata = EventMetaDataOf<EventType> & MongoDBReadEventMetadata> = ((document: Doc, event: ReadEvent<EventType, EventMetaDataType>) => Doc | null) | ((document: Doc, event: ReadEvent<EventType>) => Promise<Doc | null>);
27
+ type MongoDBWithNullableDocumentEvolve<Doc extends Document, EventType extends Event, EventMetaDataType extends EventMetaDataOf<EventType> & MongoDBReadEventMetadata = EventMetaDataOf<EventType> & MongoDBReadEventMetadata> = ((document: Doc | null, event: ReadEvent<EventType, EventMetaDataType>) => Doc | null) | ((document: Doc | null, event: ReadEvent<EventType>) => Promise<Doc | null>);
28
+ type MongoDBInlineProjectionOptions<Doc extends Document, EventType extends Event, EventMetaDataType extends EventMetaDataOf<EventType> & MongoDBReadEventMetadata = EventMetaDataOf<EventType> & MongoDBReadEventMetadata> = {
29
+ name?: string;
30
+ schemaVersion?: number;
31
+ canHandle: CanHandle<EventType>;
32
+ } & ({
33
+ evolve: MongoDBWithNullableDocumentEvolve<Doc, EventType, EventMetaDataType>;
34
+ } | {
35
+ evolve: MongoDBWithNotNullDocumentEvolve<Doc, EventType, EventMetaDataType>;
36
+ initialState: () => Doc;
37
+ });
38
+ declare const mongoDBInlineProjection: <Doc extends Document, EventType extends Event, EventMetaDataType extends EventMetaDataOf<EventType> & MongoDBReadEventMetadata = EventMetaDataOf<EventType> & Readonly<{
39
+ eventId: string;
40
+ streamPosition: bigint;
41
+ streamName: string;
42
+ }> & object>(options: MongoDBInlineProjectionOptions<Doc, EventType, EventMetaDataType>) => MongoDBInlineProjectionDefinition;
9
43
 
10
44
  declare const MongoDBEventStoreDefaultStreamVersion = 0n;
11
45
  type StreamType = string;
@@ -14,6 +48,10 @@ type StreamNameParts<T extends StreamType = StreamType> = {
14
48
  streamType: T;
15
49
  streamId: string;
16
50
  };
51
+ type StreamCollectionName<T extends StreamType = StreamType> = `emt:${T}`;
52
+ type StreamCollectionNameParts<T extends StreamType = StreamType> = {
53
+ streamType: T;
54
+ };
17
55
  type MongoDBReadModelMetadata = {
18
56
  name: string;
19
57
  schemaVersion: number;
@@ -24,7 +62,7 @@ type MongoDBReadModel<Doc extends Document = Document> = Doc & {
24
62
  };
25
63
  interface EventStream<EventType extends Event = Event, EventMetaDataType extends EventMetaDataOf<EventType> & MongoDBReadEventMetadata = EventMetaDataOf<EventType> & MongoDBReadEventMetadata> {
26
64
  streamName: string;
27
- events: Array<ReadEvent<EventType, EventMetaDataType>>;
65
+ messages: Array<ReadEvent<EventType, EventMetaDataType>>;
28
66
  metadata: {
29
67
  streamId: string;
30
68
  streamType: StreamType;
@@ -34,26 +72,22 @@ interface EventStream<EventType extends Event = Event, EventMetaDataType extends
34
72
  };
35
73
  projections: Record<string, MongoDBReadModel>;
36
74
  }
37
- interface MongoDBConnectionOptions {
38
- connectionString: string;
39
- database: string;
40
- collection?: string;
41
- }
42
75
  type MongoDBReadEventMetadata = ReadEventMetadataWithoutGlobalPosition<bigint>;
43
76
  type MongoDBReadEvent<EventType extends Event = Event> = ReadEvent<EventType, MongoDBReadEventMetadata>;
44
77
  type MongoDBEventStoreOptions = {
45
- collection: Collection<EventStream>;
78
+ database?: string;
79
+ collection?: string;
46
80
  projections?: ProjectionRegistration<'inline', MongoDBReadEventMetadata, MongoDBProjectionInlineHandlerContext>[];
81
+ } & ({
82
+ client: MongoClient;
83
+ } | {
84
+ connectionString: string;
85
+ clientOptions?: MongoClientOptions;
86
+ });
87
+ type MongoDBEventStore = EventStore<MongoDBReadEventMetadata> & {
88
+ close: () => Promise<void>;
47
89
  };
48
- declare class MongoDBEventStore implements EventStore<MongoDBReadEventMetadata> {
49
- private readonly collection;
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>;
55
- }
56
- declare const getMongoDBEventStore: (options: ConstructorParameters<typeof MongoDBEventStore>[0]) => MongoDBEventStore;
90
+ declare const getMongoDBEventStore: (options: MongoDBEventStoreOptions) => MongoDBEventStore;
57
91
  /**
58
92
  * Accepts a `streamType` (the type/category of the event stream) and an `streamId`
59
93
  * (the individual entity/object or aggregate ID) and combines them to a singular
@@ -65,5 +99,14 @@ declare function toStreamName<T extends StreamType>(streamType: T, streamId: str
65
99
  * `streamType` and `streamId`.
66
100
  */
67
101
  declare function fromStreamName<T extends StreamType>(streamName: StreamName<T>): StreamNameParts<T>;
102
+ /**
103
+ * Accepts a `streamType` (the type/category of the event stream)
104
+ * and combines them to a `collectionName` which can be used in `EventStore`.
105
+ */
106
+ declare function toStreamCollectionName<T extends StreamType>(streamType: T): StreamCollectionName<T>;
107
+ /**
108
+ * Accepts a fully formatted `streamCollectionName` and returns the parsed `streamType`.
109
+ */
110
+ declare function fromStreamCollectionName<T extends StreamType>(streamCollectionName: StreamCollectionName<T>): StreamCollectionNameParts<T>;
68
111
 
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 };
112
+ export { type EventStream, type InlineProjectionHandlerOptions, type MongoDBEventStore, MongoDBEventStoreDefaultStreamVersion, type MongoDBEventStoreOptions, type MongoDBInlineProjectionDefinition, type MongoDBInlineProjectionHandler, type MongoDBInlineProjectionOptions, type MongoDBProjectionInlineHandlerContext, type MongoDBReadEvent, type MongoDBReadEventMetadata, type MongoDBReadModel, type MongoDBReadModelMetadata, type MongoDBWithNotNullDocumentEvolve, type MongoDBWithNullableDocumentEvolve, type StreamCollectionName, type StreamCollectionNameParts, type StreamName, type StreamNameParts, type StreamType, fromStreamCollectionName, fromStreamName, getMongoDBEventStore, handleInlineProjections, mongoDBInlineProjection, toStreamCollectionName, toStreamName };
package/dist/index.js CHANGED
@@ -260,7 +260,9 @@ var streamTransformations = {
260
260
  var { retry: retry2 } = streamTransformations;
261
261
 
262
262
  // src/eventStore/mongoDBEventStore.ts
263
- import "mongodb";
263
+ import {
264
+ MongoClient
265
+ } from "mongodb";
264
266
  import { v4 as uuid4 } from "uuid";
265
267
 
266
268
  // src/eventStore/projections/index.ts
@@ -284,20 +286,59 @@ var handleInlineProjections = async (options) => {
284
286
  });
285
287
  }
286
288
  };
289
+ var mongoDBInlineProjection = (options) => {
290
+ const projectionName = options.name ?? "_default";
291
+ const schemaVersion = options.schemaVersion ?? 1;
292
+ return {
293
+ name: projectionName,
294
+ canHandle: options.canHandle,
295
+ handle: async (events, { document, updates }) => {
296
+ if (events.length === 0) return;
297
+ let state = "initialState" in options ? document ?? options.initialState() : document;
298
+ for (const event of events) {
299
+ state = await options.evolve(
300
+ state,
301
+ event
302
+ );
303
+ }
304
+ const metadata = {
305
+ name: projectionName,
306
+ schemaVersion,
307
+ streamPosition: events[events.length - 1].metadata.streamPosition
308
+ };
309
+ updates.$set[`projections.${options.name}`] = state !== null ? {
310
+ ...state,
311
+ _metadata: metadata
312
+ } : null;
313
+ }
314
+ };
315
+ };
287
316
 
288
317
  // src/eventStore/mongoDBEventStore.ts
289
318
  var MongoDBEventStoreDefaultStreamVersion = 0n;
290
- var MongoDBEventStore = class {
291
- collection;
319
+ var MongoDBEventStoreImplementation = class {
320
+ client;
321
+ defaultOptions;
322
+ shouldManageClientLifetime;
323
+ db;
324
+ streamCollections = /* @__PURE__ */ new Map();
292
325
  inlineProjections;
326
+ isClosed = false;
293
327
  constructor(options) {
294
- this.collection = options.collection;
328
+ this.client = "client" in options ? options.client : new MongoClient(options.connectionString, options.clientOptions);
329
+ this.shouldManageClientLifetime = !("client" in options);
330
+ this.defaultOptions = {
331
+ database: options.database,
332
+ collection: options.collection
333
+ };
295
334
  this.inlineProjections = (options.projections ?? []).filter(({ type }) => type === "inline").map(
296
335
  ({ projection }) => projection
297
336
  );
298
337
  }
299
338
  async readStream(streamName, options) {
339
+ const { streamType } = fromStreamName(streamName);
300
340
  const expectedStreamVersion = options?.expectedStreamVersion;
341
+ const collection = await this.collectionFor(streamType);
301
342
  const filter2 = {
302
343
  streamName: { $eq: streamName }
303
344
  };
@@ -311,11 +352,11 @@ var MongoDBEventStore = class {
311
352
  eventsSliceArr.push(Number(options.to));
312
353
  }
313
354
  const eventsSlice = eventsSliceArr.length > 1 ? { $slice: eventsSliceArr } : 1;
314
- const stream = await this.collection.findOne(filter2, {
355
+ const stream = await collection.findOne(filter2, {
315
356
  useBigInt64: true,
316
357
  projection: {
317
358
  metadata: 1,
318
- events: eventsSlice
359
+ messages: eventsSlice
319
360
  }
320
361
  });
321
362
  if (!stream) {
@@ -331,7 +372,7 @@ var MongoDBEventStore = class {
331
372
  MongoDBEventStoreDefaultStreamVersion
332
373
  );
333
374
  return {
334
- events: stream.events,
375
+ events: stream.messages,
335
376
  currentStreamVersion: stream.metadata.streamPosition,
336
377
  streamExists: true
337
378
  };
@@ -346,8 +387,10 @@ var MongoDBEventStore = class {
346
387
  };
347
388
  }
348
389
  async appendToStream(streamName, events, options) {
390
+ const { streamId, streamType } = fromStreamName(streamName);
349
391
  const expectedStreamVersion = options?.expectedStreamVersion;
350
- const stream = await this.collection.findOne(
392
+ const collection = await this.collectionFor(streamType);
393
+ const stream = await collection.findOne(
351
394
  { streamName: { $eq: streamName } },
352
395
  {
353
396
  useBigInt64: true,
@@ -379,22 +422,28 @@ var MongoDBEventStore = class {
379
422
  }
380
423
  };
381
424
  });
425
+ const now = /* @__PURE__ */ new Date();
382
426
  const updates = {
383
- $push: { events: { $each: eventsToAppend } },
384
- $set: { "metadata.updatedAt": /* @__PURE__ */ new Date() },
385
- $inc: { "metadata.streamPosition": BigInt(events.length) }
427
+ $push: { messages: { $each: eventsToAppend } },
428
+ $set: { "metadata.updatedAt": now },
429
+ $inc: { "metadata.streamPosition": BigInt(events.length) },
430
+ $setOnInsert: {
431
+ "metadata.streamId": streamId,
432
+ "metadata.streamType": streamType,
433
+ "metadata.createdAt": now
434
+ }
386
435
  };
387
436
  if (this.inlineProjections) {
388
437
  await handleInlineProjections({
389
438
  readModels: stream?.projections ?? {},
390
439
  events: eventsToAppend,
391
440
  projections: this.inlineProjections,
392
- collection: this.collection,
441
+ collection,
393
442
  updates,
394
443
  client: {}
395
444
  });
396
445
  }
397
- const updatedStream = await this.collection.updateOne(
446
+ const updatedStream = await collection.updateOne(
398
447
  {
399
448
  streamName: { $eq: streamName },
400
449
  "metadata.streamPosition": toExpectedVersion(
@@ -415,11 +464,33 @@ var MongoDBEventStore = class {
415
464
  createdNewStream: currentStreamVersion === MongoDBEventStoreDefaultStreamVersion
416
465
  };
417
466
  }
467
+ close() {
468
+ if (this.isClosed) return Promise.resolve();
469
+ this.isClosed = true;
470
+ if (!this.shouldManageClientLifetime) return Promise.resolve();
471
+ return this.client.close();
472
+ }
473
+ getDB = async () => {
474
+ if (!this.db) {
475
+ if (!this.isClosed) await this.client.connect();
476
+ this.db = this.client.db(this.defaultOptions.database);
477
+ }
478
+ return this.db;
479
+ };
480
+ collectionFor = async (streamType) => {
481
+ const collectionName = this.defaultOptions?.collection ?? toStreamCollectionName(streamType);
482
+ let collection = this.streamCollections.get(collectionName);
483
+ if (collection) return collection;
484
+ const db = await this.getDB();
485
+ collection = db.collection(collectionName);
486
+ this.streamCollections.set(
487
+ collectionName,
488
+ collection
489
+ );
490
+ return collection;
491
+ };
418
492
  };
419
- var getMongoDBEventStore = (options) => {
420
- const eventStore = new MongoDBEventStore(options);
421
- return eventStore;
422
- };
493
+ var getMongoDBEventStore = (options) => new MongoDBEventStoreImplementation(options);
423
494
  function toExpectedVersion(expectedStreamVersion) {
424
495
  if (!expectedStreamVersion) return void 0;
425
496
  if (typeof expectedStreamVersion === "string") {
@@ -442,11 +513,23 @@ function fromStreamName(streamName) {
442
513
  streamId: parts[1]
443
514
  };
444
515
  }
516
+ function toStreamCollectionName(streamType) {
517
+ return `emt:${streamType}`;
518
+ }
519
+ function fromStreamCollectionName(streamCollectionName) {
520
+ const parts = streamCollectionName.split(":");
521
+ return {
522
+ streamType: parts[1]
523
+ };
524
+ }
445
525
  export {
446
- MongoDBEventStore,
447
526
  MongoDBEventStoreDefaultStreamVersion,
527
+ fromStreamCollectionName,
448
528
  fromStreamName,
449
529
  getMongoDBEventStore,
530
+ handleInlineProjections,
531
+ mongoDBInlineProjection,
532
+ toStreamCollectionName,
450
533
  toStreamName
451
534
  };
452
535
  //# sourceMappingURL=index.js.map