@event-driven-io/emmett-mongodb 0.23.0-alpha.8 → 0.23.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.
package/dist/index.d.ts CHANGED
@@ -1,18 +1,18 @@
1
- import { Event, EventMetaDataOf, ProjectionHandler, TypedProjectionDefinition, ReadEvent, CanHandle, ReadEventMetadataWithoutGlobalPosition, ProjectionRegistration, EventStore, Closeable } from '@event-driven-io/emmett';
1
+ import { Event, ProjectionHandler, TypedProjectionDefinition, ReadEvent, CanHandle, ThenThrows, ReadEventMetadataWithoutGlobalPosition, ProjectionRegistration, EventStore, Closeable } from '@event-driven-io/emmett';
2
2
  import { UpdateFilter, Collection, Document, MongoClient, MongoClientOptions, Filter } from 'mongodb';
3
3
 
4
4
  declare const MongoDBDefaultInlineProjectionName = "_default";
5
- type MongoDBProjectionInlineHandlerContext<EventType extends Event = Event, EventMetaDataType extends EventMetaDataOf<EventType> & MongoDBReadEventMetadata = EventMetaDataOf<EventType> & MongoDBReadEventMetadata> = {
5
+ type MongoDBProjectionInlineHandlerContext<EventType extends Event = Event, EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata> = {
6
6
  document: MongoDBReadModel | null;
7
7
  streamId: string;
8
8
  updates: UpdateFilter<EventStream<EventType, EventMetaDataType>>;
9
9
  collection: Collection<EventStream<EventType, EventMetaDataType>>;
10
10
  };
11
- type MongoDBInlineProjectionHandler<EventType extends Event = Event, EventMetaDataType extends EventMetaDataOf<EventType> & MongoDBReadEventMetadata = EventMetaDataOf<EventType> & MongoDBReadEventMetadata> = ProjectionHandler<EventType, EventMetaDataType, MongoDBProjectionInlineHandlerContext>;
12
- type MongoDBInlineProjectionDefinition<EventType extends Event = Event, EventMetaDataType extends EventMetaDataOf<EventType> & MongoDBReadEventMetadata = EventMetaDataOf<EventType> & MongoDBReadEventMetadata> = TypedProjectionDefinition<EventType, EventMetaDataType, MongoDBProjectionInlineHandlerContext> & {
11
+ type MongoDBInlineProjectionHandler<EventType extends Event = Event, EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata> = ProjectionHandler<EventType, EventMetaDataType, MongoDBProjectionInlineHandlerContext>;
12
+ type MongoDBInlineProjectionDefinition<EventType extends Event = Event, EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata> = TypedProjectionDefinition<EventType, EventMetaDataType, MongoDBProjectionInlineHandlerContext> & {
13
13
  name: string;
14
14
  };
15
- type InlineProjectionHandlerOptions<EventType extends Event = Event, EventMetaDataType extends EventMetaDataOf<EventType> & MongoDBReadEventMetadata = EventMetaDataOf<EventType> & MongoDBReadEventMetadata> = {
15
+ type InlineProjectionHandlerOptions<EventType extends Event = Event, EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata> = {
16
16
  readModels: Record<string, MongoDBReadModel>;
17
17
  events: Array<ReadEvent<EventType, EventMetaDataType>>;
18
18
  projections: MongoDBInlineProjectionDefinition<EventType, EventMetaDataType>[];
@@ -21,14 +21,10 @@ type InlineProjectionHandlerOptions<EventType extends Event = Event, EventMetaDa
21
21
  updates: UpdateFilter<EventStream<Event>>;
22
22
  client: {};
23
23
  };
24
- declare const handleInlineProjections: <EventType extends Event = Event, EventMetaDataType extends EventMetaDataOf<EventType> & MongoDBReadEventMetadata = EventMetaDataOf<EventType> & Readonly<{
25
- eventId: string;
26
- streamPosition: bigint;
27
- streamName: string;
28
- }> & object>(options: InlineProjectionHandlerOptions<EventType, EventMetaDataType>) => Promise<void>;
29
- 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>);
30
- 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>);
31
- type MongoDBInlineProjectionOptions<Doc extends Document, EventType extends Event, EventMetaDataType extends EventMetaDataOf<EventType> & MongoDBReadEventMetadata = EventMetaDataOf<EventType> & MongoDBReadEventMetadata> = {
24
+ declare const handleInlineProjections: <EventType extends Event = Event, EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata>(options: InlineProjectionHandlerOptions<EventType, EventMetaDataType>) => Promise<void>;
25
+ type MongoDBWithNotNullDocumentEvolve<Doc extends Document, EventType extends Event, EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata> = ((document: Doc, event: ReadEvent<EventType, EventMetaDataType>) => Doc | null) | ((document: Doc, event: ReadEvent<EventType>) => Promise<Doc | null>);
26
+ type MongoDBWithNullableDocumentEvolve<Doc extends Document, EventType extends Event, EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata> = ((document: Doc | null, event: ReadEvent<EventType, EventMetaDataType>) => Doc | null) | ((document: Doc | null, event: ReadEvent<EventType>) => Promise<Doc | null>);
27
+ type MongoDBInlineProjectionOptions<Doc extends Document, EventType extends Event, EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata> = {
32
28
  name?: string;
33
29
  schemaVersion?: number;
34
30
  canHandle: CanHandle<EventType>;
@@ -38,11 +34,45 @@ type MongoDBInlineProjectionOptions<Doc extends Document, EventType extends Even
38
34
  evolve: MongoDBWithNotNullDocumentEvolve<Doc, EventType, EventMetaDataType>;
39
35
  initialState: () => Doc;
40
36
  });
41
- declare const mongoDBInlineProjection: <Doc extends Document, EventType extends Event, EventMetaDataType extends EventMetaDataOf<EventType> & MongoDBReadEventMetadata = EventMetaDataOf<EventType> & Readonly<{
42
- eventId: string;
43
- streamPosition: bigint;
44
- streamName: string;
45
- }> & object>(options: MongoDBInlineProjectionOptions<Doc, EventType, EventMetaDataType>) => MongoDBInlineProjectionDefinition;
37
+ declare const mongoDBInlineProjection: <Doc extends Document, EventType extends Event, EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata>(options: MongoDBInlineProjectionOptions<Doc, EventType, EventMetaDataType>) => MongoDBInlineProjectionDefinition;
38
+
39
+ type MongoDBInlineProjectionSpecGivenEvents<StreamNameType extends StreamName, EventType extends Event> = {
40
+ streamName: StreamNameType;
41
+ events: EventType[];
42
+ };
43
+ type MongoDBInlineProjectionAssertOptions<StreamNameType extends StreamName = StreamName> = {
44
+ streamName: StreamNameType;
45
+ eventStore: MongoDBEventStore;
46
+ };
47
+ type MongoDBInlineProjectionAssert<StreamNameType extends StreamName = StreamName> = (options: MongoDBInlineProjectionAssertOptions<StreamNameType>) => Promise<void | boolean>;
48
+ type MongoDBInlineProjectionSpecOptions = {
49
+ projection: MongoDBInlineProjectionDefinition;
50
+ } & MongoDBEventStoreConnectionOptions;
51
+ type MongoDBInlineProjectionSpec<StreamNameType extends StreamName, EventType extends Event> = (givenStream: MongoDBInlineProjectionSpecGivenEvents<StreamNameType, EventType>) => {
52
+ when: (events: EventType[]) => {
53
+ then: (assert: MongoDBInlineProjectionAssert, message?: string) => Promise<void>;
54
+ thenThrows: <ErrorType extends Error = Error>(...args: Parameters<ThenThrows<ErrorType>>) => Promise<void>;
55
+ };
56
+ };
57
+ declare const MongoDBInlineProjectionSpec: {
58
+ for: <StreamNameType extends StreamName, EventType extends Event>(options: MongoDBInlineProjectionSpecOptions) => MongoDBInlineProjectionSpec<StreamNameType, EventType>;
59
+ };
60
+ declare const eventInStream: <StreamNameType extends StreamName, EventType extends Event>(streamName: StreamNameType, event: EventType) => MongoDBInlineProjectionSpecGivenEvents<StreamNameType, EventType>;
61
+ declare const eventsInStream: <StreamNameType extends StreamName, EventType extends Event>(streamName: StreamNameType, events: EventType[]) => MongoDBInlineProjectionSpecGivenEvents<StreamNameType, EventType>;
62
+ declare const expectInlineReadModel: {
63
+ toHave: <Doc extends Document, StreamNameType extends StreamName = `${string}:${string}`>(expected: Partial<MongoDBReadModel<Doc>> | null) => MongoDBInlineProjectionAssert<StreamNameType>;
64
+ toDeepEquals: <Doc extends Document, StreamNameType extends StreamName = `${string}:${string}`>(expected: MongoDBReadModel<Doc> | null) => MongoDBInlineProjectionAssert<StreamNameType>;
65
+ toMatch: <Doc extends Document, StreamNameType extends StreamName = `${string}:${string}`>(match: (readModel: MongoDBReadModel<Doc> | null) => boolean) => MongoDBInlineProjectionAssert<StreamNameType>;
66
+ notToExist: <StreamNameType extends StreamName = `${string}:${string}`>() => MongoDBInlineProjectionAssert<StreamNameType>;
67
+ toExist: () => MongoDBInlineProjectionAssert;
68
+ withName: (name: string) => {
69
+ toHave: <Doc extends Document, StreamNameType extends StreamName = `${string}:${string}`>(expected: Partial<MongoDBReadModel<Doc>> | null) => MongoDBInlineProjectionAssert<StreamNameType>;
70
+ toDeepEquals: <Doc extends Document, StreamNameType extends StreamName = `${string}:${string}`>(expected: MongoDBReadModel<Doc> | null) => MongoDBInlineProjectionAssert<StreamNameType>;
71
+ toMatch: <Doc extends Document, StreamNameType extends StreamName = `${string}:${string}`>(match: (readModel: MongoDBReadModel<Doc> | null) => boolean) => MongoDBInlineProjectionAssert<StreamNameType>;
72
+ notToExist: <StreamNameType extends StreamName = `${string}:${string}`>() => MongoDBInlineProjectionAssert<StreamNameType>;
73
+ toExist: () => MongoDBInlineProjectionAssert;
74
+ };
75
+ };
46
76
 
47
77
  type MongoDBEventStoreCollectionPerStreamTypeStorageOptions = {
48
78
  /**
@@ -106,7 +136,7 @@ type MongoDBReadModelMetadata = {
106
136
  type MongoDBReadModel<Doc extends Document = Document> = Doc & {
107
137
  _metadata: MongoDBReadModelMetadata;
108
138
  };
109
- interface EventStream<EventType extends Event = Event, EventMetaDataType extends EventMetaDataOf<EventType> & MongoDBReadEventMetadata = EventMetaDataOf<EventType> & MongoDBReadEventMetadata> {
139
+ interface EventStream<EventType extends Event = Event, EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata> {
110
140
  streamName: string;
111
141
  messages: Array<ReadEvent<EventType, EventMetaDataType>>;
112
142
  metadata: {
@@ -187,10 +217,11 @@ type MongoDBEventStoreConnectionStringOptions = {
187
217
  connectionString: string;
188
218
  clientOptions?: MongoClientOptions;
189
219
  };
220
+ type MongoDBEventStoreConnectionOptions = MongoDBEventStoreClientOptions | MongoDBEventStoreConnectionStringOptions;
190
221
  type MongoDBEventStoreOptions = {
191
222
  projections?: ProjectionRegistration<'inline', MongoDBReadEventMetadata, MongoDBProjectionInlineHandlerContext>[];
192
223
  storage?: MongoDBEventStoreStorageOptions;
193
- } & (MongoDBEventStoreClientOptions | MongoDBEventStoreConnectionStringOptions);
224
+ } & MongoDBEventStoreConnectionOptions;
194
225
  type MongoDBEventStore = EventStore<MongoDBReadEventMetadata> & {
195
226
  projections: ProjectionQueries<StreamType>;
196
227
  collectionFor: <EventType extends Event>(streamType: StreamType) => Promise<Collection<EventStream<EventType>>>;
@@ -198,7 +229,7 @@ type MongoDBEventStore = EventStore<MongoDBReadEventMetadata> & {
198
229
  /**
199
230
  * Prepends `prefix` to all object keys that don't start with a '$'
200
231
  */
201
- declare function prependMongoFilterWithProjectionPrefix<T>(obj: T, prefix: string): T;
232
+ declare function prependMongoFilterWithProjectionPrefix<T, Result = T>(obj: T, prefix: string): Result;
202
233
  declare function getMongoDBEventStore(options: MongoDBEventStoreOptions & {
203
234
  client: MongoClient;
204
235
  }): MongoDBEventStore;
@@ -226,4 +257,4 @@ declare function toStreamCollectionName<T extends StreamType>(streamType: T): St
226
257
  */
227
258
  declare function fromStreamCollectionName<T extends StreamType>(streamCollectionName: StreamCollectionName<T>): StreamCollectionNameParts<T>;
228
259
 
229
- export { DefaultMongoDBEventStoreCollectionName, DefaultMongoDBEventStoreStorageOptions, type EventStream, type InlineProjectionHandlerOptions, MongoDBDefaultInlineProjectionName, type MongoDBEventStore, type MongoDBEventStoreCollectionPerStreamTypeStorageOptions, type MongoDBEventStoreCollectionResolution, type MongoDBEventStoreCustomStorageOptions, MongoDBEventStoreDefaultStreamVersion, type MongoDBEventStoreOptions, type MongoDBEventStoreSingleCollectionStorageOptions, type MongoDBEventStoreStorage, type MongoDBEventStoreStorageOptions, 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, mongoDBEventStoreStorage, mongoDBInlineProjection, prependMongoFilterWithProjectionPrefix, toStreamCollectionName, toStreamName };
260
+ export { DefaultMongoDBEventStoreCollectionName, DefaultMongoDBEventStoreStorageOptions, type EventStream, type InlineProjectionHandlerOptions, MongoDBDefaultInlineProjectionName, type MongoDBEventStore, type MongoDBEventStoreClientOptions, type MongoDBEventStoreCollectionPerStreamTypeStorageOptions, type MongoDBEventStoreCollectionResolution, type MongoDBEventStoreConnectionOptions, type MongoDBEventStoreConnectionStringOptions, type MongoDBEventStoreCustomStorageOptions, MongoDBEventStoreDefaultStreamVersion, type MongoDBEventStoreOptions, type MongoDBEventStoreSingleCollectionStorageOptions, type MongoDBEventStoreStorage, type MongoDBEventStoreStorageOptions, type MongoDBInlineProjectionAssert, type MongoDBInlineProjectionAssertOptions, type MongoDBInlineProjectionDefinition, type MongoDBInlineProjectionHandler, type MongoDBInlineProjectionOptions, MongoDBInlineProjectionSpec, type MongoDBInlineProjectionSpecGivenEvents, type MongoDBInlineProjectionSpecOptions, type MongoDBProjectionInlineHandlerContext, type MongoDBReadEvent, type MongoDBReadEventMetadata, type MongoDBReadModel, type MongoDBReadModelMetadata, type MongoDBWithNotNullDocumentEvolve, type MongoDBWithNullableDocumentEvolve, type StreamCollectionName, type StreamCollectionNameParts, type StreamName, type StreamNameParts, type StreamType, eventInStream, eventsInStream, expectInlineReadModel, fromStreamCollectionName, fromStreamName, getMongoDBEventStore, handleInlineProjections, mongoDBEventStoreStorage, mongoDBInlineProjection, prependMongoFilterWithProjectionPrefix, toStreamCollectionName, toStreamName };
package/dist/index.js CHANGED
@@ -1,6 +1,10 @@
1
- // ../emmett/dist/chunk-AEEEXE2R.js
1
+ // ../emmett/dist/chunk-4E7QLAH5.js
2
2
  var isNumber = (val) => typeof val === "number" && val === val;
3
3
  var isString = (val) => typeof val === "string";
4
+ var isErrorConstructor = (expect) => {
5
+ return typeof expect === "function" && expect.prototype && // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
6
+ expect.prototype.constructor === expect;
7
+ };
4
8
  var EmmettError = class _EmmettError extends Error {
5
9
  errorCode;
6
10
  constructor(options) {
@@ -101,6 +105,72 @@ var NotifyAboutNoActiveReadersStream = class extends TransformStream2 {
101
105
  }
102
106
  }
103
107
  };
108
+ var hasDuplicates = (array, predicate) => {
109
+ const mapped = array.map(predicate);
110
+ const uniqueValues = new Set(mapped);
111
+ return uniqueValues.size < mapped.length;
112
+ };
113
+ var getDuplicates = (array, predicate) => {
114
+ const map2 = /* @__PURE__ */ new Map();
115
+ for (let i = 0; i < array.length; i++) {
116
+ const item = array[i];
117
+ const key = predicate(item, i, array);
118
+ if (!map2.has(key)) {
119
+ map2.set(key, []);
120
+ }
121
+ map2.get(key).push(item);
122
+ }
123
+ return Array.from(map2.values()).filter((group) => group.length > 1).flat();
124
+ };
125
+ var merge = (array, item, where, onExisting, onNotFound = () => void 0) => {
126
+ let wasFound = false;
127
+ const result = array.map((p) => {
128
+ if (!where(p)) return p;
129
+ wasFound = true;
130
+ return onExisting(p);
131
+ }).filter((p) => p !== void 0).map((p) => {
132
+ if (!p) throw Error("That should not happen");
133
+ return p;
134
+ });
135
+ if (!wasFound) {
136
+ const result2 = onNotFound();
137
+ if (result2 !== void 0) return [...array, item];
138
+ }
139
+ return result;
140
+ };
141
+ var arrayUtils = {
142
+ merge,
143
+ hasDuplicates,
144
+ getDuplicates
145
+ };
146
+ var deepEquals = (left, right) => {
147
+ if (isEquatable(left)) {
148
+ return left.equals(right);
149
+ }
150
+ if (Array.isArray(left)) {
151
+ return Array.isArray(right) && left.length === right.length && left.every((val, index) => deepEquals(val, right[index]));
152
+ }
153
+ if (typeof left !== "object" || typeof right !== "object" || left === null || right === null) {
154
+ return left === right;
155
+ }
156
+ if (Array.isArray(right)) return false;
157
+ const keys1 = Object.keys(left);
158
+ const keys2 = Object.keys(right);
159
+ if (keys1.length !== keys2.length || !keys1.every((key) => keys2.includes(key)))
160
+ return false;
161
+ for (const key in left) {
162
+ if (left[key] instanceof Function && right[key] instanceof Function)
163
+ continue;
164
+ const isEqual = deepEquals(left[key], right[key]);
165
+ if (!isEqual) {
166
+ return false;
167
+ }
168
+ }
169
+ return true;
170
+ };
171
+ var isEquatable = (left) => {
172
+ return left && typeof left === "object" && "equals" in left && typeof left["equals"] === "function";
173
+ };
104
174
  var asyncRetry = async (fn, opts) => {
105
175
  if (opts === void 0 || opts.retries === 0) return fn();
106
176
  return retry(
@@ -117,6 +187,47 @@ var asyncRetry = async (fn, opts) => {
117
187
  opts ?? { retries: 0 }
118
188
  );
119
189
  };
190
+ var ParseError = class extends Error {
191
+ constructor(text) {
192
+ super(`Cannot parse! ${text}`);
193
+ }
194
+ };
195
+ var JSONParser = {
196
+ stringify: (value, options) => {
197
+ return JSON.stringify(
198
+ options?.map ? options.map(value) : value,
199
+ //TODO: Consider adding support to DateTime and adding specific format to mark that's a bigint
200
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
201
+ (_, v) => typeof v === "bigint" ? v.toString() : v
202
+ );
203
+ },
204
+ parse: (text, options) => {
205
+ const parsed = JSON.parse(text, options?.reviver);
206
+ if (options?.typeCheck && !options?.typeCheck(parsed))
207
+ throw new ParseError(text);
208
+ return options?.map ? options.map(parsed) : parsed;
209
+ }
210
+ };
211
+ var filterProjections = (type, projections2) => {
212
+ const inlineProjections2 = projections2.filter((projection2) => projection2.type === type).map(({ projection: projection2 }) => projection2);
213
+ const duplicateRegistrations = arrayUtils.getDuplicates(
214
+ inlineProjections2,
215
+ (proj) => proj.name
216
+ );
217
+ if (duplicateRegistrations.length > 0) {
218
+ throw new EmmettError(`You cannot register multiple projections with the same name (or without the name).
219
+ Ensure that:
220
+ ${JSONParser.stringify(duplicateRegistrations)}
221
+ have different names`);
222
+ }
223
+ return inlineProjections2;
224
+ };
225
+ var inlineProjections = (definitions) => definitions.map((projection2) => ({ type: "inline", projection: projection2 }));
226
+ var asyncProjections = (definitions) => definitions.map((projection2) => ({ type: "async", projection: projection2 }));
227
+ var projections = {
228
+ inline: inlineProjections,
229
+ async: asyncProjections
230
+ };
120
231
  var filter = (filter2) => new TransformStream3({
121
232
  transform(chunk, controller) {
122
233
  if (filter2(chunk)) {
@@ -258,14 +369,41 @@ var streamTransformations = {
258
369
  waitAtMost
259
370
  };
260
371
  var { retry: retry2 } = streamTransformations;
372
+ var AssertionError = class extends Error {
373
+ constructor(message) {
374
+ super(message);
375
+ }
376
+ };
377
+ var isSubset = (superObj, subObj) => {
378
+ const sup = superObj;
379
+ const sub = subObj;
380
+ assertOk(sup);
381
+ assertOk(sub);
382
+ return Object.keys(sub).every((ele) => {
383
+ if (typeof sub[ele] == "object") {
384
+ return isSubset(sup[ele], sub[ele]);
385
+ }
386
+ return sub[ele] === sup[ele];
387
+ });
388
+ };
389
+ var assertFails = (message) => {
390
+ throw new AssertionError(message ?? "That should not ever happened, right?");
391
+ };
392
+ function assertTrue(condition, message) {
393
+ if (condition !== true)
394
+ throw new AssertionError(message ?? `Condition is false`);
395
+ }
396
+ function assertOk(obj, message) {
397
+ if (!obj) throw new AssertionError(message ?? `Condition is not truthy`);
398
+ }
261
399
 
262
400
  // src/eventStore/mongoDBEventStore.ts
263
401
  import {
264
- MongoClient
402
+ MongoClient as MongoClient2
265
403
  } from "mongodb";
266
404
  import { v4 as uuid4 } from "uuid";
267
405
 
268
- // src/eventStore/projections/index.ts
406
+ // src/eventStore/projections/mongoDBInlineProjection.ts
269
407
  var MongoDBDefaultInlineProjectionName = "_default";
270
408
  var handleInlineProjections = async (options) => {
271
409
  const {
@@ -277,10 +415,10 @@ var handleInlineProjections = async (options) => {
277
415
  readModels
278
416
  } = options;
279
417
  const eventTypes = events.map((e) => e.type);
280
- const projections = allProjections.filter(
418
+ const projections2 = allProjections.filter(
281
419
  (p) => p.canHandle.some((type) => eventTypes.includes(type))
282
420
  );
283
- for (const projection of projections) {
421
+ for (const projection of projections2) {
284
422
  await projection.handle(events, {
285
423
  document: readModels[projection.name] ?? null,
286
424
  streamId,
@@ -318,6 +456,135 @@ var mongoDBInlineProjection = (options) => {
318
456
  };
319
457
  };
320
458
 
459
+ // src/eventStore/projections/mongoDBInlineProjectionSpec.ts
460
+ import { MongoClient } from "mongodb";
461
+ var MongoDBInlineProjectionSpec = {
462
+ for: (options) => {
463
+ {
464
+ const { projection, ...connectionOptions } = options;
465
+ return (givenStream) => {
466
+ const { streamName, events: givenEvents } = givenStream;
467
+ return {
468
+ when: (events) => {
469
+ const allEvents = [...givenEvents, ...events];
470
+ const run = (eventStore) => eventStore.appendToStream(streamName, allEvents);
471
+ return {
472
+ then: async (assert, message) => {
473
+ const client = "client" in connectionOptions && connectionOptions.client ? connectionOptions.client : new MongoClient(
474
+ connectionOptions.connectionString,
475
+ connectionOptions.clientOptions
476
+ );
477
+ const eventStore = getMongoDBEventStore({
478
+ projections: projections.inline([projection]),
479
+ client
480
+ });
481
+ try {
482
+ await run(eventStore);
483
+ const succeeded = await assert({ eventStore, streamName });
484
+ if (succeeded !== void 0 && succeeded === false)
485
+ assertFails(
486
+ message ?? "Projection specification didn't match the criteria"
487
+ );
488
+ } finally {
489
+ await client.close();
490
+ }
491
+ },
492
+ thenThrows: async (...args) => {
493
+ const client = "client" in connectionOptions && connectionOptions.client ? connectionOptions.client : new MongoClient(
494
+ connectionOptions.connectionString,
495
+ connectionOptions.clientOptions
496
+ );
497
+ const eventStore = getMongoDBEventStore({
498
+ projections: projections.inline([projection]),
499
+ client
500
+ });
501
+ try {
502
+ await run(eventStore);
503
+ throw new AssertionError("Handler did not fail as expected");
504
+ } catch (error) {
505
+ if (error instanceof AssertionError) throw error;
506
+ if (args.length === 0) return;
507
+ if (!isErrorConstructor(args[0])) {
508
+ assertTrue(
509
+ args[0](error),
510
+ `Error didn't match the error condition: ${error?.toString()}`
511
+ );
512
+ return;
513
+ }
514
+ assertTrue(
515
+ error instanceof args[0],
516
+ `Caught error is not an instance of the expected type: ${error?.toString()}`
517
+ );
518
+ if (args[1]) {
519
+ assertTrue(
520
+ args[1](error),
521
+ `Error didn't match the error condition: ${error?.toString()}`
522
+ );
523
+ }
524
+ } finally {
525
+ await client.close();
526
+ }
527
+ }
528
+ };
529
+ }
530
+ };
531
+ };
532
+ }
533
+ }
534
+ };
535
+ var eventInStream = (streamName, event) => ({
536
+ streamName,
537
+ events: [event]
538
+ });
539
+ var eventsInStream = (streamName, events) => ({
540
+ streamName,
541
+ events
542
+ });
543
+ var expectReadModelToMatch = async (options) => {
544
+ const { streamName, projectionName, eventStore, match } = options;
545
+ const readModel = await eventStore.projections.inline.findOne({
546
+ streamName,
547
+ projectionName
548
+ });
549
+ return match(readModel);
550
+ };
551
+ var expectInlineReadModelWithName = (projectionName) => ({
552
+ toHave: (expected) => ({ eventStore, streamName }) => expectReadModelToMatch({
553
+ eventStore,
554
+ streamName,
555
+ projectionName,
556
+ match: (readModel) => isSubset(readModel, expected)
557
+ }),
558
+ toDeepEquals: (expected) => ({ eventStore, streamName }) => expectReadModelToMatch({
559
+ eventStore,
560
+ streamName,
561
+ projectionName,
562
+ match: (readModel) => deepEquals(readModel, expected)
563
+ }),
564
+ toMatch: (match) => ({ eventStore, streamName }) => expectReadModelToMatch({
565
+ eventStore,
566
+ streamName,
567
+ projectionName,
568
+ match
569
+ }),
570
+ notToExist: () => ({ eventStore, streamName }) => expectReadModelToMatch({
571
+ eventStore,
572
+ streamName,
573
+ projectionName,
574
+ match: (readModel) => readModel === null
575
+ }),
576
+ toExist: () => ({ eventStore, streamName }) => expectReadModelToMatch({
577
+ eventStore,
578
+ streamName,
579
+ projectionName,
580
+ match: (readModel) => readModel !== null
581
+ })
582
+ });
583
+ var expectInlineReadModel = {
584
+ withName: (name) => expectInlineReadModelWithName(name),
585
+ ...expectInlineReadModelWithName(MongoDBDefaultInlineProjectionName)
586
+ };
587
+
321
588
  // src/eventStore/storage/mongoDBEventStoreStorage.ts
322
589
  var DefaultMongoDBEventStoreStorageOptions = "COLLECTION_PER_STREAM_TYPE";
323
590
  var DefaultMongoDBEventStoreCollectionName = "emt:streams";
@@ -399,14 +666,15 @@ var MongoDBEventStoreImplementation = class {
399
666
  projections;
400
667
  storage;
401
668
  constructor(options) {
402
- this.client = "client" in options && options.client ? options.client : new MongoClient(options.connectionString, options.clientOptions);
669
+ this.client = "client" in options && options.client ? options.client : new MongoClient2(options.connectionString, options.clientOptions);
403
670
  this.shouldManageClientLifetime = !("client" in options);
404
671
  this.storage = mongoDBEventStoreStorage({
405
672
  storage: options.storage,
406
673
  getConnectedClient: () => this.getConnectedClient()
407
674
  });
408
- this.inlineProjections = (options.projections ?? []).filter(({ type }) => type === "inline").map(
409
- ({ projection }) => projection
675
+ this.inlineProjections = filterProjections(
676
+ "inline",
677
+ options.projections ?? []
410
678
  );
411
679
  this.projections = {
412
680
  inline: {
@@ -499,7 +767,7 @@ var MongoDBEventStoreImplementation = class {
499
767
  data: event.data,
500
768
  metadata: {
501
769
  ...metadata,
502
- ...event.metadata ?? {}
770
+ ..."metadata" in event ? event.metadata ?? {} : {}
503
771
  }
504
772
  };
505
773
  });
@@ -566,15 +834,13 @@ var MongoDBEventStoreImplementation = class {
566
834
  async findOneInlineProjection(streamFilter, projectionQuery) {
567
835
  const { projectionName, streamName, streamType } = parseSingleProjectionQueryStreamFilter(streamFilter);
568
836
  const collection = await this.storage.collectionFor(streamType);
569
- const query = prependMongoFilterWithProjectionPrefix(
570
- // @ts-expect-error we are turning the `Filter<ProjectSchema>` into a `Filter<EventStream>`
571
- projectionQuery,
572
- `projections.${projectionName}`
573
- );
837
+ const query = prependMongoFilterWithProjectionPrefix(projectionQuery, `projections.${projectionName}`);
574
838
  const filters = [
575
- query,
576
839
  { [`projections.${projectionName}`]: { $exists: true } }
577
840
  ];
841
+ if (query) {
842
+ filters.push(query);
843
+ }
578
844
  if (streamName) {
579
845
  filters.push({ streamName: { $eq: streamName } });
580
846
  }
@@ -593,15 +859,13 @@ var MongoDBEventStoreImplementation = class {
593
859
  const { projectionName, streamNames, streamType } = parsedStreamFilter;
594
860
  const collection = await this.storage.collectionFor(streamType);
595
861
  const prefix = `projections.${projectionName}`;
596
- const projectionFilter = prependMongoFilterWithProjectionPrefix(
597
- // @ts-expect-error we are turning the `Filter<ProjectSchema>` into a `Filter<EventStream>`
598
- projectionQuery,
599
- prefix
600
- );
862
+ const projectionFilter = prependMongoFilterWithProjectionPrefix(projectionQuery, prefix);
601
863
  const filters = [
602
- projectionFilter,
603
864
  { [`projections.${projectionName}`]: { $exists: true } }
604
865
  ];
866
+ if (projectionFilter) {
867
+ filters.push(projectionFilter);
868
+ }
605
869
  if (streamNames) {
606
870
  filters.push({ streamName: { $in: streamNames } });
607
871
  }
@@ -634,15 +898,13 @@ var MongoDBEventStoreImplementation = class {
634
898
  const { projectionName, streamNames, streamType } = parsedStreamFilter;
635
899
  const collection = await this.storage.collectionFor(streamType);
636
900
  const prefix = `projections.${projectionName}`;
637
- const projectionFilter = prependMongoFilterWithProjectionPrefix(
638
- // @ts-expect-error we are turning the `Filter<ProjectSchema>` into a `Filter<EventStream>`
639
- projectionQuery,
640
- prefix
641
- );
901
+ const projectionFilter = prependMongoFilterWithProjectionPrefix(projectionQuery, prefix);
642
902
  const filters = [
643
- projectionFilter,
644
903
  { [`projections.${projectionName}`]: { $exists: true } }
645
904
  ];
905
+ if (projectionFilter) {
906
+ filters.push(projectionFilter);
907
+ }
646
908
  if (streamNames) {
647
909
  filters.push({ streamName: { $in: streamNames } });
648
910
  }
@@ -707,7 +969,7 @@ function parseMultiProjectionQueryStreamFilter(streamFilter) {
707
969
  };
708
970
  }
709
971
  function prependMongoFilterWithProjectionPrefix(obj, prefix) {
710
- if (typeof obj !== "object" || obj === null) {
972
+ if (typeof obj !== "object" || obj === null || obj === void 0) {
711
973
  return obj;
712
974
  }
713
975
  if (Array.isArray(obj)) {
@@ -763,6 +1025,10 @@ export {
763
1025
  DefaultMongoDBEventStoreStorageOptions,
764
1026
  MongoDBDefaultInlineProjectionName,
765
1027
  MongoDBEventStoreDefaultStreamVersion,
1028
+ MongoDBInlineProjectionSpec,
1029
+ eventInStream,
1030
+ eventsInStream,
1031
+ expectInlineReadModel,
766
1032
  fromStreamCollectionName,
767
1033
  fromStreamName,
768
1034
  getMongoDBEventStore,