@event-driven-io/emmett-mongodb 0.43.0-beta.14 → 0.43.0-beta.15

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["MongoClient","projections","AssertionError","MongoClient","ExpectedVersionConflictError"],"sources":["../src/eventStore/projections/mongoDBInlineProjection.ts","../src/eventStore/projections/mongoDBInlineProjectionSpec.ts","../src/eventStore/storage/mongoDBEventStoreStorage.ts","../src/eventStore/mongoDBEventStore.ts"],"sourcesContent":["import type {\n CanHandle,\n Event,\n ProjectionDefinition,\n ProjectionHandler,\n ReadEvent,\n} from '@event-driven-io/emmett';\nimport type { Collection, Document, UpdateFilter } from 'mongodb';\nimport type {\n EventStream,\n MongoDBReadEventMetadata,\n MongoDBReadModel,\n MongoDBReadModelMetadata,\n} from '../mongoDBEventStore';\n\nexport const MongoDBDefaultInlineProjectionName = '_default';\n\nexport type MongoDBProjectionInlineHandlerContext<\n EventType extends Event = Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n> = {\n document: MongoDBReadModel | null;\n streamId: string;\n updates: UpdateFilter<EventStream<EventType, EventMetaDataType>>;\n collection: Collection<EventStream<EventType, EventMetaDataType>>;\n};\n\nexport type MongoDBInlineProjectionHandler<\n EventType extends Event = Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n> = ProjectionHandler<\n EventType,\n EventMetaDataType,\n MongoDBProjectionInlineHandlerContext\n>;\n\nexport type MongoDBInlineProjectionDefinition<\n EventType extends Event = Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n> = ProjectionDefinition<\n EventType,\n EventMetaDataType,\n MongoDBProjectionInlineHandlerContext\n> & { name: string };\n\nexport type InlineProjectionHandlerOptions<\n EventType extends Event = Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n> = {\n readModels: Record<string, MongoDBReadModel>;\n events: Array<ReadEvent<EventType, EventMetaDataType>>;\n projections: MongoDBInlineProjectionDefinition<\n EventType,\n EventMetaDataType\n >[];\n streamId: string;\n collection: Collection<EventStream>;\n updates: UpdateFilter<EventStream<Event>>;\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n client: {\n //todo: add client here\n };\n};\n\nexport const handleInlineProjections = async <\n EventType extends Event = Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n>(\n options: InlineProjectionHandlerOptions<EventType, EventMetaDataType>,\n): Promise<void> => {\n const {\n events,\n projections: allProjections,\n updates: update,\n streamId,\n collection,\n readModels,\n } = options;\n\n const eventTypes = events.map((e) => e.type);\n\n const projections = allProjections.filter((p) =>\n p.canHandle.some((type) => eventTypes.includes(type)),\n );\n\n for (const projection of projections) {\n await projection.handle(events, {\n document: readModels[projection.name] ?? null,\n streamId,\n collection,\n updates: update,\n });\n }\n};\n\nexport type MongoDBWithNotNullDocumentEvolve<\n Doc extends Document,\n EventType extends Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n> =\n | ((\n document: Doc,\n event: ReadEvent<EventType, EventMetaDataType>,\n ) => Doc | null)\n | ((document: Doc, event: ReadEvent<EventType>) => Promise<Doc | null>);\n\nexport type MongoDBWithNullableDocumentEvolve<\n Doc extends Document,\n EventType extends Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n> =\n | ((\n document: Doc | null,\n event: ReadEvent<EventType, EventMetaDataType>,\n ) => Doc | null)\n | ((\n document: Doc | null,\n event: ReadEvent<EventType>,\n ) => Promise<Doc | null>);\n\nexport type MongoDBInlineProjectionOptions<\n Doc extends Document,\n EventType extends Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n> = {\n name?: string;\n schemaVersion?: number;\n canHandle: CanHandle<EventType>;\n} & (\n | {\n evolve: MongoDBWithNullableDocumentEvolve<\n Doc,\n EventType,\n EventMetaDataType\n >;\n }\n | {\n evolve: MongoDBWithNotNullDocumentEvolve<\n Doc,\n EventType,\n EventMetaDataType\n >;\n initialState: () => Doc;\n }\n);\n\nexport const mongoDBInlineProjection = <\n Doc extends Document,\n EventType extends Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n>(\n options: MongoDBInlineProjectionOptions<Doc, EventType, EventMetaDataType>,\n): MongoDBInlineProjectionDefinition => {\n const projectionName = options.name ?? MongoDBDefaultInlineProjectionName;\n const schemaVersion = options.schemaVersion ?? 1;\n\n return {\n name: projectionName,\n canHandle: options.canHandle,\n handle: async (events, { document, updates, streamId }) => {\n if (events.length === 0) return;\n\n let state =\n 'initialState' in options\n ? (document ?? options.initialState())\n : document;\n\n for (const event of events) {\n state = await options.evolve(\n state as Doc,\n event as ReadEvent<EventType, EventMetaDataType>,\n );\n }\n\n const metadata: MongoDBReadModelMetadata = {\n streamId,\n name: projectionName,\n schemaVersion,\n streamPosition: events[events.length - 1]!.metadata.streamPosition,\n };\n\n updates.$set![`projections.${projectionName}`] =\n state !== null\n ? {\n ...state,\n _metadata: metadata,\n }\n : null;\n },\n };\n};\n","import {\n assertFails,\n AssertionError,\n assertTrue,\n deepEquals,\n isErrorConstructor,\n isSubset,\n projections,\n type Event,\n type ThenThrows,\n} from '@event-driven-io/emmett';\nimport { MongoClient, type Document } from 'mongodb';\nimport {\n getMongoDBEventStore,\n type MongoDBEventStore,\n type MongoDBEventStoreConnectionOptions,\n type MongoDBReadModel,\n type StreamName,\n} from '../mongoDBEventStore';\nimport {\n MongoDBDefaultInlineProjectionName,\n type MongoDBInlineProjectionDefinition,\n} from './mongoDBInlineProjection';\n\nexport type MongoDBInlineProjectionSpecGivenEvents<\n StreamNameType extends StreamName,\n EventType extends Event,\n> = {\n streamName: StreamNameType;\n events: EventType[];\n};\n\nexport type MongoDBInlineProjectionSpec<\n StreamNameType extends StreamName,\n EventType extends Event,\n> = (\n givenStream: MongoDBInlineProjectionSpecGivenEvents<\n StreamNameType,\n EventType\n >,\n) => {\n when: (events: EventType[]) => {\n then: (\n assert: MongoDBInlineProjectionAssert,\n message?: string,\n ) => Promise<void>;\n thenThrows: <ErrorType extends Error = Error>(\n ...args: Parameters<ThenThrows<ErrorType>>\n ) => Promise<void>;\n };\n};\n\nexport type MongoDBInlineProjectionAssertOptions<\n StreamNameType extends StreamName = StreamName,\n> = {\n streamName: StreamNameType;\n eventStore: MongoDBEventStore;\n};\n\nexport type MongoDBInlineProjectionAssert<\n StreamNameType extends StreamName = StreamName,\n> = (\n options: MongoDBInlineProjectionAssertOptions<StreamNameType>,\n) => Promise<void | boolean>;\n\nexport type MongoDBInlineProjectionSpecOptions = {\n projection: MongoDBInlineProjectionDefinition;\n} & MongoDBEventStoreConnectionOptions;\n\nexport const MongoDBInlineProjectionSpec = {\n for: <StreamNameType extends StreamName, EventType extends Event>(\n options: MongoDBInlineProjectionSpecOptions,\n ): MongoDBInlineProjectionSpec<StreamNameType, EventType> => {\n {\n const { projection, ...connectionOptions } = options;\n\n return (\n givenStream: MongoDBInlineProjectionSpecGivenEvents<\n StreamNameType,\n EventType\n >,\n ) => {\n const { streamName, events: givenEvents } = givenStream;\n return {\n when: (events: EventType[]) => {\n const allEvents = [...givenEvents, ...events];\n\n const run = (eventStore: MongoDBEventStore) =>\n eventStore.appendToStream(streamName, allEvents);\n\n return {\n then: async (\n assert: MongoDBInlineProjectionAssert,\n message?: string,\n ): Promise<void> => {\n const client =\n 'client' in connectionOptions && connectionOptions.client\n ? connectionOptions.client\n : new MongoClient(\n connectionOptions.connectionString,\n connectionOptions.clientOptions,\n );\n\n const eventStore = getMongoDBEventStore({\n projections: projections.inline([projection]),\n client,\n });\n\n try {\n await run(eventStore);\n\n const succeeded = await assert({ eventStore, streamName });\n\n if (succeeded !== undefined && succeeded === false)\n assertFails(\n message ??\n \"Projection specification didn't match the criteria\",\n );\n } finally {\n await client.close();\n }\n },\n thenThrows: async <ErrorType extends Error>(\n ...args: Parameters<ThenThrows<ErrorType>>\n ): Promise<void> => {\n const client =\n 'client' in connectionOptions && connectionOptions.client\n ? connectionOptions.client\n : new MongoClient(\n connectionOptions.connectionString,\n connectionOptions.clientOptions,\n );\n\n const eventStore = getMongoDBEventStore({\n projections: projections.inline([projection]),\n client,\n });\n\n try {\n await run(eventStore);\n throw new AssertionError('Handler did not fail as expected');\n } catch (error) {\n if (error instanceof AssertionError) throw error;\n\n if (args.length === 0) return;\n\n if (!isErrorConstructor(args[0])) {\n assertTrue(\n args[0](error as ErrorType),\n `Error didn't match the error condition: ${error?.toString()}`,\n );\n return;\n }\n\n assertTrue(\n error instanceof args[0],\n `Caught error is not an instance of the expected type: ${error?.toString()}`,\n );\n\n if (args[1]) {\n assertTrue(\n args[1](error as ErrorType),\n `Error didn't match the error condition: ${error?.toString()}`,\n );\n }\n } finally {\n await client.close();\n }\n },\n };\n },\n };\n };\n }\n },\n};\n\nexport const eventInStream = <\n StreamNameType extends StreamName,\n EventType extends Event,\n>(\n streamName: StreamNameType,\n event: EventType,\n): MongoDBInlineProjectionSpecGivenEvents<StreamNameType, EventType> => ({\n streamName,\n events: [event],\n});\n\nexport const eventsInStream = <\n StreamNameType extends StreamName,\n EventType extends Event,\n>(\n streamName: StreamNameType,\n events: EventType[],\n): MongoDBInlineProjectionSpecGivenEvents<StreamNameType, EventType> => ({\n streamName,\n events,\n});\n\nconst expectReadModelToMatch = async <\n Doc extends Document = Document,\n StreamNameType extends StreamName = StreamName,\n>(\n options: MongoDBInlineProjectionAssertOptions<StreamNameType> & {\n projectionName: string;\n match: (readModel: MongoDBReadModel<Doc> | null) => boolean;\n },\n) => {\n const { streamName, projectionName, eventStore, match } = options;\n const readModel = await eventStore.projections.inline.findOne<Doc>({\n streamName,\n projectionName,\n });\n\n return match(readModel);\n};\n\nconst expectInlineReadModelWithName = (projectionName: string) => ({\n toHave:\n <Doc extends Document, StreamNameType extends StreamName = StreamName>(\n expected: Partial<MongoDBReadModel<Doc>> | null,\n ): MongoDBInlineProjectionAssert<StreamNameType> =>\n ({ eventStore, streamName }) =>\n expectReadModelToMatch<Doc>({\n eventStore,\n streamName,\n projectionName,\n match: (readModel) => isSubset(readModel, expected),\n }),\n toDeepEquals:\n <Doc extends Document, StreamNameType extends StreamName = StreamName>(\n expected: MongoDBReadModel<Doc> | null,\n ): MongoDBInlineProjectionAssert<StreamNameType> =>\n ({ eventStore, streamName }) =>\n expectReadModelToMatch<Doc>({\n eventStore,\n streamName,\n projectionName,\n match: (readModel) => deepEquals(readModel, expected),\n }),\n toMatch:\n <Doc extends Document, StreamNameType extends StreamName = StreamName>(\n match: (readModel: MongoDBReadModel<Doc> | null) => boolean,\n ): MongoDBInlineProjectionAssert<StreamNameType> =>\n ({ eventStore, streamName }) =>\n expectReadModelToMatch<Doc>({\n eventStore,\n streamName,\n projectionName,\n match,\n }),\n notToExist:\n <\n StreamNameType extends StreamName = StreamName,\n >(): MongoDBInlineProjectionAssert<StreamNameType> =>\n ({ eventStore, streamName }) =>\n expectReadModelToMatch({\n eventStore,\n streamName,\n projectionName,\n match: (readModel) => readModel === null,\n }),\n toExist:\n (): MongoDBInlineProjectionAssert =>\n ({ eventStore, streamName }) =>\n expectReadModelToMatch({\n eventStore,\n streamName,\n projectionName,\n match: (readModel) => readModel !== null,\n }),\n});\n\nexport const expectInlineReadModel = {\n withName: (name: string) => expectInlineReadModelWithName(name),\n ...expectInlineReadModelWithName(MongoDBDefaultInlineProjectionName),\n};\n","import type { Event } from '@event-driven-io/emmett';\nimport type { Collection, Db, MongoClient } from 'mongodb';\nimport {\n toStreamCollectionName,\n type EventStream,\n type StreamType,\n} from '../mongoDBEventStore';\n\nexport type MongoDBEventStoreCollectionPerStreamTypeStorageOptions = {\n /**\n * The recommended setting where each stream type will be kept\n * in a separate collection type using the format: `emt_${streamType}`.\n */\n type: 'COLLECTION_PER_STREAM_TYPE';\n databaseName?: string;\n};\n\nexport type MongoDBEventStoreSingleCollectionStorageOptions = {\n /**\n * All streams will be kept withing a single MongDB collection\n * It'll either use default collection name (\"emt_streams\")\n * or provided name through 'collection' param.\n */\n type: 'SINGLE_COLLECTION';\n collectionName?: string;\n databaseName?: string;\n};\n\nexport type MongoDBEventStoreCollectionResolution = {\n databaseName?: string;\n collectionName: string;\n};\n\nexport type MongoDBEventStoreCustomStorageOptions = {\n /**\n * This is advanced option, where you specify your own collection\n * resolution function. You can do that by specifying the `collectionFor` function.\n */\n type: 'CUSTOM';\n databaseName?: string;\n collectionFor: <T extends StreamType>(\n streamType: T,\n ) => string | MongoDBEventStoreCollectionResolution;\n};\n\nexport type MongoDBEventStoreStorageOptions =\n | 'COLLECTION_PER_STREAM_TYPE'\n | 'SINGLE_COLLECTION'\n | MongoDBEventStoreSingleCollectionStorageOptions\n | MongoDBEventStoreCollectionPerStreamTypeStorageOptions\n | MongoDBEventStoreCustomStorageOptions;\n\nexport const DefaultMongoDBEventStoreStorageOptions =\n 'COLLECTION_PER_STREAM_TYPE';\n\nexport type MongoDBEventStoreStorage = {\n collectionFor: <T extends StreamType, EventType extends Event = Event>(\n streamType: T,\n ) => Promise<Collection<EventStream<EventType>>>;\n};\n\nexport const DefaultMongoDBEventStoreCollectionName = 'emt:streams';\n\nconst resolveCollectionAndDatabase = <T extends StreamType>(\n streamType: T,\n options: MongoDBEventStoreStorageOptions,\n): MongoDBEventStoreCollectionResolution => {\n if (\n options === 'SINGLE_COLLECTION' ||\n (typeof options === 'object' && options.type === 'SINGLE_COLLECTION')\n ) {\n return {\n collectionName:\n typeof options === 'object'\n ? (options.collectionName ?? DefaultMongoDBEventStoreCollectionName)\n : DefaultMongoDBEventStoreCollectionName,\n databaseName:\n typeof options === 'object' ? options.databaseName : undefined,\n };\n } else if (\n options === 'COLLECTION_PER_STREAM_TYPE' ||\n (typeof options === 'object' &&\n options.type === 'COLLECTION_PER_STREAM_TYPE')\n ) {\n return {\n collectionName: toStreamCollectionName(streamType),\n databaseName:\n typeof options === 'object' ? options.databaseName : undefined,\n };\n } else {\n const result = options.collectionFor(streamType);\n return {\n collectionName:\n typeof result === 'object' ? result.collectionName : result,\n databaseName:\n typeof result === 'object'\n ? (result.databaseName ?? options.databaseName)\n : options.databaseName,\n };\n }\n};\n\nconst getDB = async (options: {\n databaseName: string | undefined;\n dbsCache: Map<string, Db>;\n getConnectedClient: () => Promise<MongoClient>;\n}): Promise<Db> => {\n const { dbsCache, databaseName, getConnectedClient } = options;\n const safeDbName = databaseName ?? '___default';\n\n let db = dbsCache.get(safeDbName);\n\n if (!db) {\n const connectedClient = await getConnectedClient();\n\n db = connectedClient.db(databaseName);\n\n dbsCache.set(safeDbName, db);\n }\n\n return db;\n};\n\nconst collectionFor = async <EventType extends Event = Event>(options: {\n collectionName: string;\n streamCollections: Map<string, Collection<EventStream>>;\n db: Db;\n}): Promise<Collection<EventStream<EventType>>> => {\n const { collectionName, db, streamCollections } = options;\n\n let collection = streamCollections.get(collectionName) as\n | Collection<EventStream<EventType>>\n | undefined;\n\n if (!collection) {\n collection = db.collection<EventStream<EventType>>(collectionName);\n await collection.createIndex({ streamName: 1 }, { unique: true });\n\n streamCollections.set(\n collectionName,\n collection as Collection<EventStream>,\n );\n }\n\n return collection;\n};\n\nexport const mongoDBEventStoreStorage = (options: {\n storage?: MongoDBEventStoreStorageOptions | undefined;\n getConnectedClient: () => Promise<MongoClient>;\n}): MongoDBEventStoreStorage => {\n const dbsCache: Map<string, Db> = new Map();\n const streamCollections: Map<string, Collection<EventStream>> = new Map();\n const storageOptions =\n options.storage ?? DefaultMongoDBEventStoreStorageOptions;\n\n const { getConnectedClient } = options;\n\n return {\n collectionFor: async <\n T extends StreamType,\n EventType extends Event = Event,\n >(\n streamType: T,\n ): Promise<Collection<EventStream<EventType>>> => {\n const { collectionName, databaseName } = resolveCollectionAndDatabase(\n streamType,\n storageOptions,\n );\n\n let collection = streamCollections.get(collectionName) as\n | Collection<EventStream<EventType>>\n | undefined;\n\n if (!collection) {\n const db = await getDB({ databaseName, dbsCache, getConnectedClient });\n collection = await collectionFor<EventType>({\n collectionName,\n streamCollections,\n db,\n });\n }\n\n return collection;\n },\n };\n};\n","import {\n assertExpectedVersionMatchesCurrent,\n downcastRecordedMessage,\n ExpectedVersionConflictError,\n filterProjections,\n tryPublishMessagesAfterCommit,\n upcastRecordedMessages,\n type AggregateStreamOptions,\n type AggregateStreamResult,\n type AppendToStreamOptions,\n type AppendToStreamResult,\n type Closeable,\n type DefaultEventStoreOptions,\n type Event,\n type EventStore,\n type ProjectionRegistration,\n type ReadEvent,\n type ReadEventMetadataWithoutGlobalPosition,\n type ReadStreamOptions,\n type ReadStreamResult,\n type StreamExistsResult,\n} from '@event-driven-io/emmett';\nimport {\n MongoClient,\n type Collection,\n type Document,\n type Filter,\n type MongoClientOptions,\n type Sort,\n type UpdateFilter,\n type WithId,\n} from 'mongodb';\nimport { v4 as uuid } from 'uuid';\nimport {\n handleInlineProjections,\n MongoDBDefaultInlineProjectionName,\n type MongoDBInlineProjectionDefinition,\n type MongoDBProjectionInlineHandlerContext,\n} from './projections';\nimport {\n mongoDBEventStoreStorage,\n type MongoDBEventStoreStorage,\n type MongoDBEventStoreStorageOptions,\n} from './storage';\n\nexport const MongoDBEventStoreDefaultStreamVersion = 0n;\n\nexport type StreamType = string;\nexport type StreamName<T extends StreamType = StreamType> = `${T}:${string}`;\n\nexport type StreamNameParts<T extends StreamType = StreamType> = {\n streamType: T;\n streamId: string;\n};\n\nexport type StreamCollectionName<T extends StreamType = StreamType> =\n `emt:${T}`;\n\nexport type StreamCollectionNameParts<T extends StreamType = StreamType> = {\n streamType: T;\n};\n\nexport type MongoDBReadModelMetadata = {\n streamId: string;\n name: string;\n schemaVersion: number;\n streamPosition: bigint;\n};\n\nexport type MongoDBReadModel<Doc extends Document = Document> = Doc & {\n _metadata: MongoDBReadModelMetadata;\n};\n\nexport interface EventStream<\n EventType extends Event = Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n> {\n streamName: string;\n messages: Array<ReadEvent<EventType, EventMetaDataType>>;\n metadata: {\n streamId: string;\n streamType: StreamType;\n streamPosition: bigint;\n createdAt: Date;\n updatedAt: Date;\n };\n projections: Record<string, MongoDBReadModel>;\n}\n\nexport type MongoDBReadEventMetadata = ReadEventMetadataWithoutGlobalPosition;\n\nexport type MongoDBReadEvent<EventType extends Event = Event> = ReadEvent<\n EventType,\n MongoDBReadEventMetadata\n>;\n\ntype SingleProjectionQueryStreamFilter<T extends StreamType> = {\n projectionName?: string;\n} & ({ streamName: StreamName<T> } | { streamType: T; streamId?: string });\n\ntype MultiProjectionQueryStreamFilter<T extends StreamType> = {\n projectionName?: string;\n} & (\n | { streamNames: StreamName<T>[] }\n | { streamType: T; streamIds?: string[] }\n);\n\ntype MultiProjectionQueryOptions = {\n skip?: number;\n limit?: number;\n sort?: [string, 1 | -1][] | Record<string, 1 | -1>;\n};\n\n/**\n * Helpers for querying inline projections on event streams.\n */\ntype InlineProjectionQueries<T extends StreamType> = {\n /**\n * Helper for querying for a single projection. Similar to `collection.findOne`.\n * @param streamFilter - A filter object for stream level fields. If `streamType` is required if `streamName` is not provided. If `projectionName` is not provided, the default projection will be used (`MongoDBDefaultInlineProjectionName`).\n * @param projectionQuery - A MongoDB filter query based on the projection schema. Internally, this function will prepend each object key with the necessary projection name.\n */\n findOne: <Doc extends Document>(\n streamFilter: SingleProjectionQueryStreamFilter<T>,\n projectionQuery?: Filter<MongoDBReadModel<Doc>>,\n ) => Promise<MongoDBReadModel<Doc> | null>;\n /**\n * Helper for querying for multiple projections. Similar to `collection.find`.\n *\n * ***NOTE***: If `streamFilter.streamNames` is an empty array, this function will return an empty array. If `streamFilter.streamIds` is an empty array, the `streamIds` filter will not be used.\n *\n * @param streamFilter - A filter object for stream level fields. If `streamType` is required if `streamNames` is not provided. If `projectionName` is not provided, the default projection will be used (`MongoDBDefaultInlineProjectionName`).\n * @param projectionQuery - A MongoDB filter query based on the projection schema. Internally, this function will prepend each object key with the necessary projection name.\n * @param queryOptions - Additional query options like `skip`, `limit`, and `sort`. `sort`, similar to `projectionQuery`, will prepend each object key with the necessary projection name.\n */\n find: <Doc extends Document>(\n streamFilter: MultiProjectionQueryStreamFilter<T>,\n projectionQuery?: Filter<MongoDBReadModel<Doc>>,\n queryOptions?: MultiProjectionQueryOptions,\n ) => Promise<MongoDBReadModel<Doc>[]>;\n /**\n * Returns the total number of documents matching the provided filter options. Similar to `collection.countDocuments`.\n *\n * ***NOTE***: If `streamFilter.streamNames` is an empty array, this function will return `0`. If `streamFilter.streamIds` is an empty array, the `streamIds` filter will not be used.\n *\n * @param streamFilter - A filter object for stream level fields. If `streamType` is required if `streamNames` is not provided. If `projectionName` is not provided, the default projection will be used (`MongoDBDefaultInlineProjectionName`).\n * @param projectionQuery - A MongoDB filter query based on the projection schema. Internally, this function will prepend each object key with the necessary projection name.\n */\n count: <Doc extends Document>(\n streamFilter: MultiProjectionQueryStreamFilter<T>,\n projectionQuery?: Filter<MongoDBReadModel<Doc>>,\n ) => Promise<number>;\n};\n\n/**\n * Helpers for querying projections on event streams.\n */\ntype ProjectionQueries<T extends StreamType> = {\n inline: InlineProjectionQueries<T>;\n};\n\nexport type MongoDBEventStoreClientOptions = {\n client: MongoClient;\n connectionString?: never;\n clientOptions?: never;\n};\n\nexport type MongoDBEventStoreConnectionStringOptions = {\n client?: never;\n connectionString: string;\n clientOptions?: MongoClientOptions;\n};\n\nexport type MongoDBEventStoreConnectionOptions =\n | MongoDBEventStoreClientOptions\n | MongoDBEventStoreConnectionStringOptions;\n\nexport type MongoDBEventStoreOptions = {\n projections?: ProjectionRegistration<\n 'inline',\n MongoDBReadEventMetadata,\n MongoDBProjectionInlineHandlerContext\n >[];\n storage?: MongoDBEventStoreStorageOptions;\n} & MongoDBEventStoreConnectionOptions &\n DefaultEventStoreOptions<MongoDBEventStore>;\n\nexport type MongoDBEventStore = EventStore<MongoDBReadEventMetadata> & {\n projections: ProjectionQueries<StreamType>;\n collectionFor: <EventType extends Event>(\n streamType: StreamType,\n ) => Promise<Collection<EventStream<EventType>>>;\n};\n\nclass MongoDBEventStoreImplementation implements MongoDBEventStore, Closeable {\n private readonly client: MongoClient;\n private readonly inlineProjections: MongoDBInlineProjectionDefinition[];\n private shouldManageClientLifetime: boolean;\n private isClosed: boolean = false;\n private storage: MongoDBEventStoreStorage;\n private options: MongoDBEventStoreOptions;\n public projections: ProjectionQueries<StreamType>;\n\n constructor(options: MongoDBEventStoreOptions) {\n this.options = options;\n this.client =\n 'client' in options && options.client\n ? options.client\n : new MongoClient(options.connectionString, options.clientOptions);\n this.shouldManageClientLifetime = !('client' in options);\n this.storage = mongoDBEventStoreStorage({\n storage: options.storage,\n getConnectedClient: () => this.getConnectedClient(),\n });\n this.inlineProjections = filterProjections(\n 'inline',\n options.projections ?? [],\n ) as MongoDBInlineProjectionDefinition[];\n\n this.projections = {\n inline: {\n findOne: this.findOneInlineProjection.bind(this),\n find: this.findInlineProjection.bind(this),\n count: this.countInlineProjection.bind(this),\n },\n };\n }\n\n async readStream<\n EventType extends Event,\n EventPayloadType extends Event = EventType,\n >(\n streamName: StreamName,\n options?: ReadStreamOptions<EventType, EventPayloadType>,\n ): Promise<\n Exclude<ReadStreamResult<EventType, MongoDBReadEventMetadata>, null>\n > {\n const { streamType } = fromStreamName(streamName);\n const expectedStreamVersion = options?.expectedStreamVersion;\n\n const collection = await this.storage.collectionFor(streamType);\n\n const filter = {\n streamName: { $eq: streamName },\n };\n\n const eventsSliceArr: number[] = [];\n\n if (options && 'from' in options) {\n eventsSliceArr.push(Number(options.from));\n } else {\n eventsSliceArr.push(0);\n }\n\n if (options && 'to' in options) {\n eventsSliceArr.push(Number(options.to));\n }\n\n const eventsSlice =\n eventsSliceArr.length > 1 ? { $slice: eventsSliceArr } : 1;\n\n const stream = await collection.findOne<\n WithId<Pick<EventStream<EventPayloadType>, 'metadata' | 'messages'>>\n >(filter, {\n useBigInt64: true,\n projection: {\n metadata: 1,\n messages: eventsSlice,\n },\n });\n\n if (!stream) {\n return {\n events: [],\n currentStreamVersion: MongoDBEventStoreDefaultStreamVersion,\n streamExists: false,\n };\n }\n\n assertExpectedVersionMatchesCurrent(\n stream.metadata.streamPosition,\n expectedStreamVersion,\n MongoDBEventStoreDefaultStreamVersion,\n );\n\n const events = upcastRecordedMessages(\n stream.messages,\n options?.schema?.versioning,\n );\n\n return {\n events,\n currentStreamVersion: stream.metadata.streamPosition,\n streamExists: true,\n };\n }\n\n async aggregateStream<\n State,\n EventType extends Event,\n EventPayloadType extends Event = EventType,\n >(\n streamName: StreamName,\n options: AggregateStreamOptions<\n State,\n EventType,\n MongoDBReadEventMetadata,\n EventPayloadType\n >,\n ): Promise<AggregateStreamResult<State>> {\n const stream = await this.readStream<EventType, EventPayloadType>(\n streamName,\n options?.read,\n );\n const { evolve, initialState } = options;\n\n const state = stream.events.reduce(evolve, initialState());\n return {\n state,\n currentStreamVersion: stream.currentStreamVersion,\n streamExists: stream.streamExists,\n };\n }\n\n async appendToStream<\n EventType extends Event,\n EventPayloadType extends Event = EventType,\n >(\n streamName: StreamName,\n events: EventType[],\n options?: AppendToStreamOptions<EventType, EventPayloadType>,\n ): Promise<AppendToStreamResult> {\n const { streamId, streamType } = fromStreamName(streamName);\n const expectedStreamVersion = options?.expectedStreamVersion;\n\n const collection = await this.storage.collectionFor(streamType);\n\n const stream = await collection.findOne<\n WithId<Pick<EventStream<EventPayloadType>, 'metadata' | 'projections'>>\n >(\n { streamName: { $eq: streamName } },\n {\n useBigInt64: true,\n projection: {\n 'metadata.streamPosition': 1,\n projections: 1,\n },\n },\n );\n\n const currentStreamVersion =\n stream?.metadata.streamPosition ?? MongoDBEventStoreDefaultStreamVersion;\n\n assertExpectedVersionMatchesCurrent(\n currentStreamVersion,\n expectedStreamVersion,\n MongoDBEventStoreDefaultStreamVersion,\n );\n\n let streamOffset = currentStreamVersion;\n\n const eventsToAppend: ReadEvent<\n EventPayloadType,\n MongoDBReadEventMetadata\n >[] = events.map((event) => {\n const metadata: MongoDBReadEventMetadata = {\n messageId: uuid(),\n streamName,\n streamPosition: ++streamOffset,\n };\n return downcastRecordedMessage(\n {\n type: event.type,\n data: event.data,\n metadata: {\n ...metadata,\n ...('metadata' in event ? (event.metadata ?? {}) : {}),\n },\n } as ReadEvent<EventType, MongoDBReadEventMetadata>,\n options?.schema?.versioning,\n );\n });\n\n const now = new Date();\n const updates: UpdateFilter<EventStream> = {\n $push: { messages: { $each: eventsToAppend } },\n $set: {\n 'metadata.updatedAt': now,\n 'metadata.streamPosition': currentStreamVersion + BigInt(events.length),\n },\n $setOnInsert: {\n streamName,\n 'metadata.streamId': streamId,\n 'metadata.streamType': streamType,\n 'metadata.createdAt': now,\n },\n };\n\n if (this.inlineProjections) {\n await handleInlineProjections({\n readModels: stream?.projections ?? {},\n streamId,\n events: eventsToAppend,\n projections: this.inlineProjections,\n collection,\n updates,\n client: {},\n });\n }\n\n const updatedStream = await collection.updateOne(\n {\n streamName: { $eq: streamName },\n 'metadata.streamPosition': currentStreamVersion,\n },\n updates,\n { useBigInt64: true, upsert: true },\n );\n\n if (!updatedStream) {\n throw new ExpectedVersionConflictError(\n currentStreamVersion,\n options?.expectedStreamVersion ?? 0n,\n );\n }\n\n await tryPublishMessagesAfterCommit<MongoDBEventStore>(\n eventsToAppend,\n this.options.hooks,\n // {\n // TODO: same context as InlineProjectionHandlerContext for mongodb?\n // },\n );\n\n return {\n nextExpectedStreamVersion:\n currentStreamVersion + BigInt(eventsToAppend.length),\n createdNewStream:\n currentStreamVersion === MongoDBEventStoreDefaultStreamVersion,\n };\n }\n\n async streamExists(streamName: StreamName): Promise<StreamExistsResult> {\n const { streamType } = fromStreamName(streamName);\n\n const collection = await this.storage.collectionFor(streamType);\n\n const filter = {\n streamName: { $eq: streamName },\n };\n\n const count = await collection.countDocuments(filter, {\n useBigInt64: true,\n limit: 1,\n });\n\n return Boolean(count > 0);\n }\n\n collectionFor = async <EventType extends Event>(\n streamType: StreamType,\n ): Promise<Collection<EventStream<EventType>>> => {\n return this.storage.collectionFor(streamType);\n };\n\n /**\n * Gracefully cleans up managed resources by the MongoDBEventStore.\n * It closes MongoDB client created for the provided connection string\n * through event store options.\n *\n * @memberof Closeable\n */\n close = (): Promise<void> => {\n if (this.isClosed) return Promise.resolve();\n\n this.isClosed = true;\n if (!this.shouldManageClientLifetime) return Promise.resolve();\n\n return this.client.close();\n };\n\n private async findOneInlineProjection<Doc extends Document>(\n streamFilter: SingleProjectionQueryStreamFilter<StreamType>,\n projectionQuery?: Filter<MongoDBReadModel<Doc>>,\n ) {\n const { projectionName, streamName, streamType } =\n parseSingleProjectionQueryStreamFilter(streamFilter);\n const collection = await this.storage.collectionFor(streamType);\n const query = prependMongoFilterWithProjectionPrefix<\n Filter<MongoDBReadModel<Doc>> | undefined,\n Filter<EventStream> | undefined\n >(projectionQuery, `projections.${projectionName}`);\n\n const filters: Filter<EventStream>[] = [\n { [`projections.${projectionName}`]: { $exists: true } },\n ];\n\n if (query) {\n filters.push(query);\n }\n\n if (streamName) {\n filters.push({ streamName: { $eq: streamName } });\n }\n\n const result = await collection.findOne<{\n projections: Record<typeof projectionName, MongoDBReadModel<Doc>>;\n }>(\n { $and: filters },\n {\n useBigInt64: true,\n projection: { [`projections.${projectionName}`]: 1 },\n },\n );\n\n return result?.projections?.[projectionName] ?? null;\n }\n\n private async findInlineProjection<Doc extends Document>(\n streamFilter: MultiProjectionQueryStreamFilter<StreamType>,\n projectionQuery?: Filter<MongoDBReadModel<Doc>>,\n queryOptions?: MultiProjectionQueryOptions,\n ) {\n const parsedStreamFilter =\n parseMultiProjectionQueryStreamFilter(streamFilter);\n if (!parsedStreamFilter) return [];\n const { projectionName, streamNames, streamType } = parsedStreamFilter;\n\n const collection = await this.storage.collectionFor(streamType);\n const prefix = `projections.${projectionName}`;\n const projectionFilter = prependMongoFilterWithProjectionPrefix<\n Filter<MongoDBReadModel<Doc>> | undefined,\n Filter<EventStream> | undefined\n >(projectionQuery, prefix);\n\n const filters: Filter<EventStream>[] = [\n { [`projections.${projectionName}`]: { $ne: null } },\n ];\n\n if (projectionFilter) {\n filters.push(projectionFilter);\n }\n\n if (streamNames) {\n filters.push({ streamName: { $in: streamNames } });\n }\n\n let query = collection.find<\n EventStream & {\n projections: Record<typeof projectionName, MongoDBReadModel<Doc>>;\n }\n >(\n { $and: filters },\n {\n useBigInt64: true,\n projection: { [`projections.${projectionName}`]: 1 },\n },\n );\n\n if (queryOptions?.skip) {\n query = query.skip(queryOptions.skip);\n }\n\n if (queryOptions?.limit) {\n query = query.limit(queryOptions.limit);\n }\n\n if (queryOptions?.sort) {\n const sort = prependMongoFilterWithProjectionPrefix<Sort>(\n queryOptions.sort,\n prefix,\n );\n query = query.sort(sort);\n }\n\n const streams = await query.toArray();\n\n return streams\n .map((s) => s.projections[projectionName])\n .filter((p): p is MongoDBReadModel<Doc> => !!p);\n }\n\n private async countInlineProjection<Doc extends Document>(\n streamFilter: MultiProjectionQueryStreamFilter<StreamType>,\n projectionQuery?: Filter<MongoDBReadModel<Doc>>,\n ) {\n const parsedStreamFilter =\n parseMultiProjectionQueryStreamFilter(streamFilter);\n if (!parsedStreamFilter) return 0;\n const { projectionName, streamNames, streamType } = parsedStreamFilter;\n\n const collection = await this.storage.collectionFor(streamType);\n const prefix = `projections.${projectionName}`;\n const projectionFilter = prependMongoFilterWithProjectionPrefix<\n Filter<MongoDBReadModel<Doc>> | undefined,\n Filter<EventStream> | undefined\n >(projectionQuery, prefix);\n\n const filters: Filter<EventStream>[] = [\n { [`projections.${projectionName}`]: { $ne: null } },\n ];\n\n if (projectionFilter) {\n filters.push(projectionFilter);\n }\n\n if (streamNames) {\n filters.push({ streamName: { $in: streamNames } });\n }\n\n const total = await collection.countDocuments({ $and: filters });\n return total;\n }\n\n private getConnectedClient = async (): Promise<MongoClient> => {\n if (!this.isClosed) await this.client.connect();\n return this.client;\n };\n}\n\nfunction parseSingleProjectionQueryStreamFilter<\n T extends StreamType = StreamType,\n>(streamFilter: SingleProjectionQueryStreamFilter<T>) {\n const projectionName =\n streamFilter.projectionName ?? MongoDBDefaultInlineProjectionName;\n\n if ('streamName' in streamFilter) {\n const { streamType } = fromStreamName(streamFilter.streamName);\n return {\n projectionName,\n streamName: streamFilter.streamName,\n streamType,\n };\n }\n\n if (streamFilter.streamId) {\n const streamName = toStreamName(\n streamFilter.streamType,\n streamFilter.streamId,\n );\n return {\n projectionName,\n streamName,\n streamType: streamFilter.streamType,\n };\n }\n\n return {\n projectionName,\n streamType: streamFilter.streamType,\n };\n}\n\nfunction parseMultiProjectionQueryStreamFilter<T extends StreamType>(\n streamFilter: MultiProjectionQueryStreamFilter<T>,\n) {\n const projectionName =\n streamFilter.projectionName ?? MongoDBDefaultInlineProjectionName;\n\n if ('streamNames' in streamFilter) {\n if (streamFilter.streamNames.length == 0) return null;\n const { streamType } = fromStreamName(streamFilter.streamNames[0]!);\n return {\n projectionName,\n streamNames: streamFilter.streamNames,\n streamType,\n };\n }\n\n if (streamFilter.streamIds && streamFilter.streamIds.length > 0) {\n const streamNames = streamFilter.streamIds.map((id) =>\n toStreamName(streamFilter.streamType, id),\n );\n return {\n projectionName,\n streamNames,\n streamType: streamFilter.streamType,\n };\n }\n\n return {\n projectionName,\n streamType: streamFilter.streamType,\n };\n}\n\n/**\n * Prepends `prefix` to all object keys that don't start with a '$'\n */\nexport function prependMongoFilterWithProjectionPrefix<T, Result = T>(\n obj: T,\n prefix: string,\n): Result {\n if (typeof obj !== 'object' || obj === null || obj === undefined) {\n return obj as unknown as Result;\n }\n\n if (Array.isArray(obj)) {\n for (let i = 0; i < obj.length; i++) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n obj[i] = prependMongoFilterWithProjectionPrefix(obj[i], prefix);\n }\n return obj as unknown as Result;\n }\n\n for (const key in obj) {\n // @ts-expect-error we're forcing `k` to be a key of `T`\n const k: keyof typeof obj = addProjectionPrefixToMongoKey(key, prefix);\n if (k !== key) {\n obj[k] = obj[key as keyof typeof obj];\n delete obj[key as keyof typeof obj];\n }\n\n obj[k] = prependMongoFilterWithProjectionPrefix(obj[k], prefix);\n }\n\n return obj as unknown as Result;\n}\n\nfunction addProjectionPrefixToMongoKey(key: string, prefix: string): string {\n // MongoDB operators\n if (key[0] === '$') {\n return key;\n }\n\n return `${prefix}${key.length > 0 ? '.' : ''}${key}`;\n}\n\nexport function getMongoDBEventStore(\n options: MongoDBEventStoreOptions & { client: MongoClient },\n): MongoDBEventStore;\n\nexport function getMongoDBEventStore(\n options: MongoDBEventStoreOptions & { connectionString: string },\n): MongoDBEventStore & Closeable;\n\n// Implementation signature covers both, using a union for `options`\nexport function getMongoDBEventStore(\n options: MongoDBEventStoreOptions,\n): MongoDBEventStore | Closeable {\n const impl = new MongoDBEventStoreImplementation(options);\n\n // If a client is provided externally, we don't want to allow closing it\n if ('client' in options && 'close' in impl) {\n delete (impl as Partial<MongoDBEventStoreImplementation>).close;\n }\n\n return impl;\n}\n\n/**\n * Accepts a `streamType` (the type/category of the event stream) and an `streamId`\n * (the individual entity/object or aggregate ID) and combines them to a singular\n * `streamName` which can be used in `EventStore`.\n */\nexport function toStreamName<T extends StreamType>(\n streamType: T,\n streamId: string,\n): StreamName<T> {\n return `${streamType}:${streamId}`;\n}\n\n/**\n * Accepts a fully formatted `streamName` and returns the broken down\n * `streamType` and `streamId`.\n */\nexport function fromStreamName<T extends StreamType>(\n streamName: StreamName<T>,\n): StreamNameParts<T> {\n const parts = streamName.split(':') as [T, string];\n return {\n streamType: parts[0],\n streamId: parts[1],\n };\n}\n\n/**\n * Accepts a `streamType` (the type/category of the event stream)\n * and combines them to a `collectionName` which can be used in `EventStore`.\n */\nexport function toStreamCollectionName<T extends StreamType>(\n streamType: T,\n): StreamCollectionName<T> {\n return `emt:${streamType}`;\n}\n\n/**\n * Accepts a fully formatted `streamCollectionName` and returns the parsed `streamType`.\n */\nexport function fromStreamCollectionName<T extends StreamType>(\n streamCollectionName: StreamCollectionName<T>,\n): StreamCollectionNameParts<T> {\n const parts = streamCollectionName.split(':') as [string, T];\n return {\n streamType: parts[1],\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["MongoClient","projections","AssertionError","MongoClient","ExpectedVersionConflictError"],"sources":["../src/eventStore/projections/mongoDBInlineProjection.ts","../src/eventStore/projections/mongoDBInlineProjectionSpec.ts","../src/eventStore/storage/mongoDBEventStoreStorage.ts","../src/eventStore/mongoDBEventStore.ts"],"sourcesContent":["import type {\n CanHandle,\n Event,\n ProjectionDefinition,\n ProjectionHandler,\n ReadEvent,\n} from '@event-driven-io/emmett';\nimport type { Collection, Document, UpdateFilter } from 'mongodb';\nimport type {\n EventStream,\n MongoDBReadEventMetadata,\n MongoDBReadModel,\n MongoDBReadModelMetadata,\n} from '../mongoDBEventStore';\n\nexport const MongoDBDefaultInlineProjectionName = '_default';\n\nexport type MongoDBProjectionInlineHandlerContext<\n EventType extends Event = Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n> = {\n document: MongoDBReadModel | null;\n streamId: string;\n updates: UpdateFilter<EventStream<EventType, EventMetaDataType>>;\n collection: Collection<EventStream<EventType, EventMetaDataType>>;\n};\n\nexport type MongoDBInlineProjectionHandler<\n EventType extends Event = Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n> = ProjectionHandler<\n EventType,\n EventMetaDataType,\n MongoDBProjectionInlineHandlerContext\n>;\n\nexport type MongoDBInlineProjectionDefinition<\n EventType extends Event = Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n> = ProjectionDefinition<\n EventType,\n EventMetaDataType,\n MongoDBProjectionInlineHandlerContext\n> & { name: string };\n\nexport type InlineProjectionHandlerOptions<\n EventType extends Event = Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n> = {\n readModels: Record<string, MongoDBReadModel>;\n events: Array<ReadEvent<EventType, EventMetaDataType>>;\n projections: MongoDBInlineProjectionDefinition<\n EventType,\n EventMetaDataType\n >[];\n streamId: string;\n collection: Collection<EventStream>;\n updates: UpdateFilter<EventStream<Event>>;\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n client: {\n //todo: add client here\n };\n};\n\nexport const handleInlineProjections = async <\n EventType extends Event = Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n>(\n options: InlineProjectionHandlerOptions<EventType, EventMetaDataType>,\n): Promise<void> => {\n const {\n events,\n projections: allProjections,\n updates: update,\n streamId,\n collection,\n readModels,\n } = options;\n\n const eventTypes = events.map((e) => e.type);\n\n const projections = allProjections.filter((p) =>\n p.canHandle.some((type) => eventTypes.includes(type)),\n );\n\n for (const projection of projections) {\n await projection.handle(events, {\n document: readModels[projection.name] ?? null,\n streamId,\n collection,\n updates: update,\n });\n }\n};\n\nexport type MongoDBWithNotNullDocumentEvolve<\n Doc extends Document,\n EventType extends Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n> =\n | ((\n document: Doc,\n event: ReadEvent<EventType, EventMetaDataType>,\n ) => Doc | null)\n | ((document: Doc, event: ReadEvent<EventType>) => Promise<Doc | null>);\n\nexport type MongoDBWithNullableDocumentEvolve<\n Doc extends Document,\n EventType extends Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n> =\n | ((\n document: Doc | null,\n event: ReadEvent<EventType, EventMetaDataType>,\n ) => Doc | null)\n | ((\n document: Doc | null,\n event: ReadEvent<EventType>,\n ) => Promise<Doc | null>);\n\nexport type MongoDBInlineProjectionOptions<\n Doc extends Document,\n EventType extends Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n> = {\n name?: string;\n schemaVersion?: number;\n canHandle: CanHandle<EventType>;\n} & (\n | {\n evolve: MongoDBWithNullableDocumentEvolve<\n Doc,\n EventType,\n EventMetaDataType\n >;\n }\n | {\n evolve: MongoDBWithNotNullDocumentEvolve<\n Doc,\n EventType,\n EventMetaDataType\n >;\n initialState: () => Doc;\n }\n);\n\nexport const mongoDBInlineProjection = <\n Doc extends Document,\n EventType extends Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n>(\n options: MongoDBInlineProjectionOptions<Doc, EventType, EventMetaDataType>,\n): MongoDBInlineProjectionDefinition => {\n const projectionName = options.name ?? MongoDBDefaultInlineProjectionName;\n const schemaVersion = options.schemaVersion ?? 1;\n\n return {\n name: projectionName,\n canHandle: options.canHandle,\n handle: async (events, { document, updates, streamId }) => {\n if (events.length === 0) return;\n\n let state =\n 'initialState' in options\n ? (document ?? options.initialState())\n : document;\n\n for (const event of events) {\n state = await options.evolve(\n state as Doc,\n event as ReadEvent<EventType, EventMetaDataType>,\n );\n }\n\n const metadata: MongoDBReadModelMetadata = {\n streamId,\n name: projectionName,\n schemaVersion,\n streamPosition: events[events.length - 1]!.metadata.streamPosition,\n };\n\n updates.$set![`projections.${projectionName}`] =\n state !== null\n ? {\n ...state,\n _metadata: metadata,\n }\n : null;\n },\n };\n};\n","import {\n assertFails,\n AssertionError,\n assertTrue,\n deepEquals,\n isErrorConstructor,\n isSubset,\n projections,\n type Event,\n type ThenThrows,\n} from '@event-driven-io/emmett';\nimport { MongoClient, type Document } from 'mongodb';\nimport {\n getMongoDBEventStore,\n type MongoDBEventStore,\n type MongoDBEventStoreConnectionOptions,\n type MongoDBReadModel,\n type StreamName,\n} from '../mongoDBEventStore';\nimport {\n MongoDBDefaultInlineProjectionName,\n type MongoDBInlineProjectionDefinition,\n} from './mongoDBInlineProjection';\n\nexport type MongoDBInlineProjectionSpecGivenEvents<\n StreamNameType extends StreamName,\n EventType extends Event,\n> = {\n streamName: StreamNameType;\n events: EventType[];\n};\n\nexport type MongoDBInlineProjectionSpec<\n StreamNameType extends StreamName,\n EventType extends Event,\n> = (\n givenStream: MongoDBInlineProjectionSpecGivenEvents<\n StreamNameType,\n EventType\n >,\n) => {\n when: (events: EventType[]) => {\n then: (\n assert: MongoDBInlineProjectionAssert,\n message?: string,\n ) => Promise<void>;\n thenThrows: <ErrorType extends Error = Error>(\n ...args: Parameters<ThenThrows<ErrorType>>\n ) => Promise<void>;\n };\n};\n\nexport type MongoDBInlineProjectionAssertOptions<\n StreamNameType extends StreamName = StreamName,\n> = {\n streamName: StreamNameType;\n eventStore: MongoDBEventStore;\n};\n\nexport type MongoDBInlineProjectionAssert<\n StreamNameType extends StreamName = StreamName,\n> = (\n options: MongoDBInlineProjectionAssertOptions<StreamNameType>,\n) => Promise<void | boolean>;\n\nexport type MongoDBInlineProjectionSpecOptions = {\n projection: MongoDBInlineProjectionDefinition;\n} & MongoDBEventStoreConnectionOptions;\n\nexport const MongoDBInlineProjectionSpec = {\n for: <StreamNameType extends StreamName, EventType extends Event>(\n options: MongoDBInlineProjectionSpecOptions,\n ): MongoDBInlineProjectionSpec<StreamNameType, EventType> => {\n {\n const { projection, ...connectionOptions } = options;\n\n return (\n givenStream: MongoDBInlineProjectionSpecGivenEvents<\n StreamNameType,\n EventType\n >,\n ) => {\n const { streamName, events: givenEvents } = givenStream;\n return {\n when: (events: EventType[]) => {\n const allEvents = [...givenEvents, ...events];\n\n const run = (eventStore: MongoDBEventStore) =>\n eventStore.appendToStream(streamName, allEvents);\n\n return {\n then: async (\n assert: MongoDBInlineProjectionAssert,\n message?: string,\n ): Promise<void> => {\n const client =\n 'client' in connectionOptions && connectionOptions.client\n ? connectionOptions.client\n : new MongoClient(\n connectionOptions.connectionString,\n connectionOptions.clientOptions,\n );\n\n const eventStore = getMongoDBEventStore({\n projections: projections.inline([projection]),\n client,\n });\n\n try {\n await run(eventStore);\n\n const succeeded = await assert({ eventStore, streamName });\n\n if (succeeded !== undefined && succeeded === false)\n assertFails(\n message ??\n \"Projection specification didn't match the criteria\",\n );\n } finally {\n await client.close();\n }\n },\n thenThrows: async <ErrorType extends Error>(\n ...args: Parameters<ThenThrows<ErrorType>>\n ): Promise<void> => {\n const client =\n 'client' in connectionOptions && connectionOptions.client\n ? connectionOptions.client\n : new MongoClient(\n connectionOptions.connectionString,\n connectionOptions.clientOptions,\n );\n\n const eventStore = getMongoDBEventStore({\n projections: projections.inline([projection]),\n client,\n });\n\n try {\n await run(eventStore);\n throw new AssertionError('Handler did not fail as expected');\n } catch (error) {\n if (error instanceof AssertionError) throw error;\n\n if (args.length === 0) return;\n\n if (!isErrorConstructor(args[0])) {\n assertTrue(\n args[0](error as ErrorType),\n `Error didn't match the error condition: ${error?.toString()}`,\n );\n return;\n }\n\n assertTrue(\n error instanceof args[0],\n `Caught error is not an instance of the expected type: ${error?.toString()}`,\n );\n\n if (args[1]) {\n assertTrue(\n args[1](error as ErrorType),\n `Error didn't match the error condition: ${error?.toString()}`,\n );\n }\n } finally {\n await client.close();\n }\n },\n };\n },\n };\n };\n }\n },\n};\n\nexport const eventInStream = <\n StreamNameType extends StreamName,\n EventType extends Event,\n>(\n streamName: StreamNameType,\n event: EventType,\n): MongoDBInlineProjectionSpecGivenEvents<StreamNameType, EventType> => ({\n streamName,\n events: [event],\n});\n\nexport const eventsInStream = <\n StreamNameType extends StreamName,\n EventType extends Event,\n>(\n streamName: StreamNameType,\n events: EventType[],\n): MongoDBInlineProjectionSpecGivenEvents<StreamNameType, EventType> => ({\n streamName,\n events,\n});\n\nconst expectReadModelToMatch = async <\n Doc extends Document = Document,\n StreamNameType extends StreamName = StreamName,\n>(\n options: MongoDBInlineProjectionAssertOptions<StreamNameType> & {\n projectionName: string;\n match: (readModel: MongoDBReadModel<Doc> | null) => boolean;\n },\n) => {\n const { streamName, projectionName, eventStore, match } = options;\n const readModel = await eventStore.projections.inline.findOne<Doc>({\n streamName,\n projectionName,\n });\n\n return match(readModel);\n};\n\nconst expectInlineReadModelWithName = (projectionName: string) => ({\n toHave:\n <Doc extends Document, StreamNameType extends StreamName = StreamName>(\n expected: Partial<MongoDBReadModel<Doc>> | null,\n ): MongoDBInlineProjectionAssert<StreamNameType> =>\n ({ eventStore, streamName }) =>\n expectReadModelToMatch<Doc>({\n eventStore,\n streamName,\n projectionName,\n match: (readModel) => isSubset(readModel, expected),\n }),\n toDeepEquals:\n <Doc extends Document, StreamNameType extends StreamName = StreamName>(\n expected: MongoDBReadModel<Doc> | null,\n ): MongoDBInlineProjectionAssert<StreamNameType> =>\n ({ eventStore, streamName }) =>\n expectReadModelToMatch<Doc>({\n eventStore,\n streamName,\n projectionName,\n match: (readModel) => deepEquals(readModel, expected),\n }),\n toMatch:\n <Doc extends Document, StreamNameType extends StreamName = StreamName>(\n match: (readModel: MongoDBReadModel<Doc> | null) => boolean,\n ): MongoDBInlineProjectionAssert<StreamNameType> =>\n ({ eventStore, streamName }) =>\n expectReadModelToMatch<Doc>({\n eventStore,\n streamName,\n projectionName,\n match,\n }),\n notToExist:\n <\n StreamNameType extends StreamName = StreamName,\n >(): MongoDBInlineProjectionAssert<StreamNameType> =>\n ({ eventStore, streamName }) =>\n expectReadModelToMatch({\n eventStore,\n streamName,\n projectionName,\n match: (readModel) => readModel === null,\n }),\n toExist:\n (): MongoDBInlineProjectionAssert =>\n ({ eventStore, streamName }) =>\n expectReadModelToMatch({\n eventStore,\n streamName,\n projectionName,\n match: (readModel) => readModel !== null,\n }),\n});\n\nexport const expectInlineReadModel = {\n withName: (name: string) => expectInlineReadModelWithName(name),\n ...expectInlineReadModelWithName(MongoDBDefaultInlineProjectionName),\n};\n","import type { Event } from '@event-driven-io/emmett';\nimport type { Collection, Db, MongoClient } from 'mongodb';\nimport {\n toStreamCollectionName,\n type EventStream,\n type StreamType,\n} from '../mongoDBEventStore';\n\nexport type MongoDBEventStoreCollectionPerStreamTypeStorageOptions = {\n /**\n * The recommended setting where each stream type will be kept\n * in a separate collection type using the format: `emt_${streamType}`.\n */\n type: 'COLLECTION_PER_STREAM_TYPE';\n databaseName?: string;\n};\n\nexport type MongoDBEventStoreSingleCollectionStorageOptions = {\n /**\n * All streams will be kept withing a single MongDB collection\n * It'll either use default collection name (\"emt_streams\")\n * or provided name through 'collection' param.\n */\n type: 'SINGLE_COLLECTION';\n collectionName?: string;\n databaseName?: string;\n};\n\nexport type MongoDBEventStoreCollectionResolution = {\n databaseName?: string;\n collectionName: string;\n};\n\nexport type MongoDBEventStoreCustomStorageOptions = {\n /**\n * This is advanced option, where you specify your own collection\n * resolution function. You can do that by specifying the `collectionFor` function.\n */\n type: 'CUSTOM';\n databaseName?: string;\n collectionFor: <T extends StreamType>(\n streamType: T,\n ) => string | MongoDBEventStoreCollectionResolution;\n};\n\nexport type MongoDBEventStoreStorageOptions =\n | 'COLLECTION_PER_STREAM_TYPE'\n | 'SINGLE_COLLECTION'\n | MongoDBEventStoreSingleCollectionStorageOptions\n | MongoDBEventStoreCollectionPerStreamTypeStorageOptions\n | MongoDBEventStoreCustomStorageOptions;\n\nexport const DefaultMongoDBEventStoreStorageOptions =\n 'COLLECTION_PER_STREAM_TYPE';\n\nexport type MongoDBEventStoreStorage = {\n collectionFor: <T extends StreamType, EventType extends Event = Event>(\n streamType: T,\n ) => Promise<Collection<EventStream<EventType>>>;\n};\n\nexport const DefaultMongoDBEventStoreCollectionName = 'emt:streams';\n\nconst resolveCollectionAndDatabase = <T extends StreamType>(\n streamType: T,\n options: MongoDBEventStoreStorageOptions,\n): MongoDBEventStoreCollectionResolution => {\n if (\n options === 'SINGLE_COLLECTION' ||\n (typeof options === 'object' && options.type === 'SINGLE_COLLECTION')\n ) {\n return {\n collectionName:\n typeof options === 'object'\n ? (options.collectionName ?? DefaultMongoDBEventStoreCollectionName)\n : DefaultMongoDBEventStoreCollectionName,\n databaseName:\n typeof options === 'object' ? options.databaseName : undefined,\n };\n } else if (\n options === 'COLLECTION_PER_STREAM_TYPE' ||\n (typeof options === 'object' &&\n options.type === 'COLLECTION_PER_STREAM_TYPE')\n ) {\n return {\n collectionName: toStreamCollectionName(streamType),\n databaseName:\n typeof options === 'object' ? options.databaseName : undefined,\n };\n } else {\n const result = options.collectionFor(streamType);\n return {\n collectionName:\n typeof result === 'object' ? result.collectionName : result,\n databaseName:\n typeof result === 'object'\n ? (result.databaseName ?? options.databaseName)\n : options.databaseName,\n };\n }\n};\n\nconst getDB = async (options: {\n databaseName: string | undefined;\n dbsCache: Map<string, Db>;\n getConnectedClient: () => Promise<MongoClient>;\n}): Promise<Db> => {\n const { dbsCache, databaseName, getConnectedClient } = options;\n const safeDbName = databaseName ?? '___default';\n\n let db = dbsCache.get(safeDbName);\n\n if (!db) {\n const connectedClient = await getConnectedClient();\n\n db = connectedClient.db(databaseName);\n\n dbsCache.set(safeDbName, db);\n }\n\n return db;\n};\n\nconst collectionFor = async <EventType extends Event = Event>(options: {\n collectionName: string;\n streamCollections: Map<string, Collection<EventStream>>;\n db: Db;\n}): Promise<Collection<EventStream<EventType>>> => {\n const { collectionName, db, streamCollections } = options;\n\n let collection = streamCollections.get(collectionName) as\n | Collection<EventStream<EventType>>\n | undefined;\n\n if (!collection) {\n collection = db.collection<EventStream<EventType>>(collectionName);\n await collection.createIndex({ streamName: 1 }, { unique: true });\n\n streamCollections.set(\n collectionName,\n collection as Collection<EventStream>,\n );\n }\n\n return collection;\n};\n\nexport const mongoDBEventStoreStorage = (options: {\n storage?: MongoDBEventStoreStorageOptions | undefined;\n getConnectedClient: () => Promise<MongoClient>;\n}): MongoDBEventStoreStorage => {\n const dbsCache: Map<string, Db> = new Map();\n const streamCollections: Map<string, Collection<EventStream>> = new Map();\n const storageOptions =\n options.storage ?? DefaultMongoDBEventStoreStorageOptions;\n\n const { getConnectedClient } = options;\n\n return {\n collectionFor: async <\n T extends StreamType,\n EventType extends Event = Event,\n >(\n streamType: T,\n ): Promise<Collection<EventStream<EventType>>> => {\n const { collectionName, databaseName } = resolveCollectionAndDatabase(\n streamType,\n storageOptions,\n );\n\n let collection = streamCollections.get(collectionName) as\n | Collection<EventStream<EventType>>\n | undefined;\n\n if (!collection) {\n const db = await getDB({ databaseName, dbsCache, getConnectedClient });\n collection = await collectionFor<EventType>({\n collectionName,\n streamCollections,\n db,\n });\n }\n\n return collection;\n },\n };\n};\n","import {\n assertExpectedVersionMatchesCurrent,\n downcastRecordedMessage,\n ExpectedVersionConflictError,\n filterProjections,\n tryPublishMessagesAfterCommit,\n upcastRecordedMessages,\n type AggregateStreamOptions,\n type AggregateStreamResult,\n type AppendToStreamOptions,\n type AppendToStreamResult,\n type Closeable,\n type DefaultEventStoreOptions,\n type Event,\n type EventStore,\n type ProjectionRegistration,\n type ReadEvent,\n type ReadEventMetadataWithoutGlobalPosition,\n type ReadStreamOptions,\n type ReadStreamResult,\n type StreamExistsResult,\n} from '@event-driven-io/emmett';\nimport {\n MongoClient,\n type Collection,\n type Document,\n type Filter,\n type MongoClientOptions,\n type Sort,\n type UpdateFilter,\n type WithId,\n} from 'mongodb';\nimport { v4 as uuid } from 'uuid';\nimport {\n handleInlineProjections,\n MongoDBDefaultInlineProjectionName,\n type MongoDBInlineProjectionDefinition,\n type MongoDBProjectionInlineHandlerContext,\n} from './projections';\nimport {\n mongoDBEventStoreStorage,\n type MongoDBEventStoreStorage,\n type MongoDBEventStoreStorageOptions,\n} from './storage';\n\nexport const MongoDBEventStoreDefaultStreamVersion = 0n;\n\nexport type StreamType = string;\nexport type StreamName<T extends StreamType = StreamType> = `${T}:${string}`;\n\nexport type StreamNameParts<T extends StreamType = StreamType> = {\n streamType: T;\n streamId: string;\n};\n\nexport type StreamCollectionName<T extends StreamType = StreamType> =\n `emt:${T}`;\n\nexport type StreamCollectionNameParts<T extends StreamType = StreamType> = {\n streamType: T;\n};\n\nexport type MongoDBReadModelMetadata = {\n streamId: string;\n name: string;\n schemaVersion: number;\n streamPosition: bigint;\n};\n\nexport type MongoDBReadModel<Doc extends Document = Document> = Doc & {\n _metadata: MongoDBReadModelMetadata;\n};\n\nexport interface EventStream<\n EventType extends Event = Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n> {\n streamName: string;\n messages: Array<ReadEvent<EventType, EventMetaDataType>>;\n metadata: {\n streamId: string;\n streamType: StreamType;\n streamPosition: bigint;\n createdAt: Date;\n updatedAt: Date;\n };\n projections: Record<string, MongoDBReadModel>;\n}\n\nexport type MongoDBReadEventMetadata = ReadEventMetadataWithoutGlobalPosition;\n\nexport type MongoDBReadEvent<EventType extends Event = Event> = ReadEvent<\n EventType,\n MongoDBReadEventMetadata\n>;\n\ntype SingleProjectionQueryStreamFilter<T extends StreamType> = {\n projectionName?: string;\n} & ({ streamName: StreamName<T> } | { streamType: T; streamId?: string });\n\ntype MultiProjectionQueryStreamFilter<T extends StreamType> = {\n projectionName?: string;\n} & (\n | { streamNames: StreamName<T>[] }\n | { streamType: T; streamIds?: string[] }\n);\n\ntype MultiProjectionQueryOptions = {\n skip?: number;\n limit?: number;\n sort?: [string, 1 | -1][] | Record<string, 1 | -1>;\n};\n\n/**\n * Helpers for querying inline projections on event streams.\n */\ntype InlineProjectionQueries<T extends StreamType> = {\n /**\n * Helper for querying for a single projection. Similar to `collection.findOne`.\n * @param streamFilter - A filter object for stream level fields. If `streamType` is required if `streamName` is not provided. If `projectionName` is not provided, the default projection will be used (`MongoDBDefaultInlineProjectionName`).\n * @param projectionQuery - A MongoDB filter query based on the projection schema. Internally, this function will prepend each object key with the necessary projection name.\n */\n findOne: <Doc extends Document>(\n streamFilter: SingleProjectionQueryStreamFilter<T>,\n projectionQuery?: Filter<MongoDBReadModel<Doc>>,\n ) => Promise<MongoDBReadModel<Doc> | null>;\n /**\n * Helper for querying for multiple projections. Similar to `collection.find`.\n *\n * ***NOTE***: If `streamFilter.streamNames` is an empty array, this function will return an empty array. If `streamFilter.streamIds` is an empty array, the `streamIds` filter will not be used.\n *\n * @param streamFilter - A filter object for stream level fields. If `streamType` is required if `streamNames` is not provided. If `projectionName` is not provided, the default projection will be used (`MongoDBDefaultInlineProjectionName`).\n * @param projectionQuery - A MongoDB filter query based on the projection schema. Internally, this function will prepend each object key with the necessary projection name.\n * @param queryOptions - Additional query options like `skip`, `limit`, and `sort`. `sort`, similar to `projectionQuery`, will prepend each object key with the necessary projection name.\n */\n find: <Doc extends Document>(\n streamFilter: MultiProjectionQueryStreamFilter<T>,\n projectionQuery?: Filter<MongoDBReadModel<Doc>>,\n queryOptions?: MultiProjectionQueryOptions,\n ) => Promise<MongoDBReadModel<Doc>[]>;\n /**\n * Returns the total number of documents matching the provided filter options. Similar to `collection.countDocuments`.\n *\n * ***NOTE***: If `streamFilter.streamNames` is an empty array, this function will return `0`. If `streamFilter.streamIds` is an empty array, the `streamIds` filter will not be used.\n *\n * @param streamFilter - A filter object for stream level fields. If `streamType` is required if `streamNames` is not provided. If `projectionName` is not provided, the default projection will be used (`MongoDBDefaultInlineProjectionName`).\n * @param projectionQuery - A MongoDB filter query based on the projection schema. Internally, this function will prepend each object key with the necessary projection name.\n */\n count: <Doc extends Document>(\n streamFilter: MultiProjectionQueryStreamFilter<T>,\n projectionQuery?: Filter<MongoDBReadModel<Doc>>,\n ) => Promise<number>;\n};\n\n/**\n * Helpers for querying projections on event streams.\n */\ntype ProjectionQueries<T extends StreamType> = {\n inline: InlineProjectionQueries<T>;\n};\n\nexport type MongoDBEventStoreClientOptions = {\n client: MongoClient;\n connectionString?: never;\n clientOptions?: never;\n};\n\nexport type MongoDBEventStoreConnectionStringOptions = {\n client?: never;\n connectionString: string;\n clientOptions?: MongoClientOptions;\n};\n\nexport type MongoDBEventStoreConnectionOptions =\n | MongoDBEventStoreClientOptions\n | MongoDBEventStoreConnectionStringOptions;\n\nexport type MongoDBEventStoreOptions = {\n projections?: ProjectionRegistration<\n 'inline',\n MongoDBReadEventMetadata,\n MongoDBProjectionInlineHandlerContext\n >[];\n storage?: MongoDBEventStoreStorageOptions;\n} & MongoDBEventStoreConnectionOptions &\n DefaultEventStoreOptions<MongoDBEventStore>;\n\nexport type MongoDBEventStore = EventStore<MongoDBReadEventMetadata> & {\n projections: ProjectionQueries<StreamType>;\n collectionFor: <EventType extends Event>(\n streamType: StreamType,\n ) => Promise<Collection<EventStream<EventType>>>;\n};\n\nclass MongoDBEventStoreImplementation implements MongoDBEventStore, Closeable {\n private readonly client: MongoClient;\n private readonly inlineProjections: MongoDBInlineProjectionDefinition[];\n private shouldManageClientLifetime: boolean;\n private isClosed: boolean = false;\n private storage: MongoDBEventStoreStorage;\n private options: MongoDBEventStoreOptions;\n public projections: ProjectionQueries<StreamType>;\n\n constructor(options: MongoDBEventStoreOptions) {\n this.options = options;\n this.client =\n 'client' in options && options.client\n ? options.client\n : new MongoClient(options.connectionString, options.clientOptions);\n this.shouldManageClientLifetime = !('client' in options);\n this.storage = mongoDBEventStoreStorage({\n storage: options.storage,\n getConnectedClient: () => this.getConnectedClient(),\n });\n this.inlineProjections = filterProjections(\n 'inline',\n options.projections ?? [],\n ) as MongoDBInlineProjectionDefinition[];\n\n this.projections = {\n inline: {\n findOne: this.findOneInlineProjection.bind(this),\n find: this.findInlineProjection.bind(this),\n count: this.countInlineProjection.bind(this),\n },\n };\n }\n\n async readStream<\n EventType extends Event,\n EventPayloadType extends Event = EventType,\n >(\n streamName: StreamName,\n options?: ReadStreamOptions<EventType, EventPayloadType>,\n ): Promise<\n Exclude<ReadStreamResult<EventType, MongoDBReadEventMetadata>, null>\n > {\n const { streamType } = fromStreamName(streamName);\n const expectedStreamVersion = options?.expectedStreamVersion;\n\n const collection = await this.storage.collectionFor(streamType);\n\n const filter = {\n streamName: { $eq: streamName },\n };\n\n const eventsSliceArr: number[] = [];\n\n if (options && 'from' in options) {\n eventsSliceArr.push(Number(options.from));\n } else {\n eventsSliceArr.push(0);\n }\n\n if (options && 'to' in options) {\n eventsSliceArr.push(Number(options.to));\n }\n\n const eventsSlice =\n eventsSliceArr.length > 1 ? { $slice: eventsSliceArr } : 1;\n\n const stream = await collection.findOne<\n WithId<Pick<EventStream<EventPayloadType>, 'metadata' | 'messages'>>\n >(filter, {\n useBigInt64: true,\n projection: {\n metadata: 1,\n messages: eventsSlice,\n },\n });\n\n if (!stream) {\n return {\n events: [],\n currentStreamVersion: MongoDBEventStoreDefaultStreamVersion,\n streamExists: false,\n };\n }\n\n assertExpectedVersionMatchesCurrent(\n stream.metadata.streamPosition,\n expectedStreamVersion,\n MongoDBEventStoreDefaultStreamVersion,\n );\n\n const events = upcastRecordedMessages(\n stream.messages,\n options?.schema?.versioning,\n );\n\n return {\n events,\n currentStreamVersion: stream.metadata.streamPosition,\n streamExists: true,\n };\n }\n\n async aggregateStream<\n State,\n EventType extends Event,\n EventPayloadType extends Event = EventType,\n >(\n streamName: StreamName,\n options: AggregateStreamOptions<\n State,\n EventType,\n MongoDBReadEventMetadata,\n EventPayloadType\n >,\n ): Promise<AggregateStreamResult<State>> {\n const stream = await this.readStream<EventType, EventPayloadType>(\n streamName,\n options?.read,\n );\n const { evolve, initialState } = options;\n\n const state = stream.events.reduce(evolve, initialState());\n return {\n state,\n currentStreamVersion: stream.currentStreamVersion,\n streamExists: stream.streamExists,\n };\n }\n\n async appendToStream<\n EventType extends Event,\n EventPayloadType extends Event = EventType,\n >(\n streamName: StreamName,\n events: EventType[],\n options?: AppendToStreamOptions<EventType, EventPayloadType>,\n ): Promise<AppendToStreamResult> {\n const { streamId, streamType } = fromStreamName(streamName);\n const expectedStreamVersion = options?.expectedStreamVersion;\n\n const collection = await this.storage.collectionFor(streamType);\n\n const stream = await collection.findOne<\n WithId<Pick<EventStream<EventPayloadType>, 'metadata' | 'projections'>>\n >(\n { streamName: { $eq: streamName } },\n {\n useBigInt64: true,\n projection: {\n 'metadata.streamPosition': 1,\n projections: 1,\n },\n },\n );\n\n const currentStreamVersion =\n stream?.metadata.streamPosition ?? MongoDBEventStoreDefaultStreamVersion;\n\n assertExpectedVersionMatchesCurrent(\n currentStreamVersion,\n expectedStreamVersion,\n MongoDBEventStoreDefaultStreamVersion,\n );\n\n let streamOffset = currentStreamVersion;\n\n const eventsToAppend: ReadEvent<\n EventPayloadType,\n MongoDBReadEventMetadata\n >[] = events.map((event) => {\n const metadata: MongoDBReadEventMetadata = {\n messageId: uuid(),\n streamName,\n streamPosition: ++streamOffset,\n };\n return downcastRecordedMessage(\n {\n type: event.type,\n data: event.data,\n metadata: {\n ...metadata,\n ...('metadata' in event ? (event.metadata ?? {}) : {}),\n },\n } as ReadEvent<EventType, MongoDBReadEventMetadata>,\n options?.schema?.versioning,\n );\n });\n\n const now = new Date();\n const updates: UpdateFilter<EventStream> = {\n $push: { messages: { $each: eventsToAppend } },\n $set: {\n 'metadata.updatedAt': now,\n 'metadata.streamPosition': currentStreamVersion + BigInt(events.length),\n },\n $setOnInsert: {\n streamName,\n 'metadata.streamId': streamId,\n 'metadata.streamType': streamType,\n 'metadata.createdAt': now,\n },\n };\n\n if (this.inlineProjections) {\n await handleInlineProjections({\n readModels: stream?.projections ?? {},\n streamId,\n events: eventsToAppend,\n projections: this.inlineProjections,\n collection,\n updates,\n client: {},\n });\n }\n\n const updatedStream = await collection.updateOne(\n {\n streamName: { $eq: streamName },\n 'metadata.streamPosition': currentStreamVersion,\n },\n updates,\n { useBigInt64: true, upsert: true },\n );\n\n if (!updatedStream) {\n throw new ExpectedVersionConflictError(\n currentStreamVersion,\n options?.expectedStreamVersion ?? 0n,\n );\n }\n\n await tryPublishMessagesAfterCommit<MongoDBEventStore>(\n eventsToAppend,\n this.options.hooks,\n // {\n // TODO: same context as InlineProjectionHandlerContext for mongodb?\n // },\n );\n\n return {\n nextExpectedStreamVersion:\n currentStreamVersion + BigInt(eventsToAppend.length),\n createdNewStream:\n currentStreamVersion === MongoDBEventStoreDefaultStreamVersion,\n };\n }\n\n async streamExists(streamName: StreamName): Promise<StreamExistsResult> {\n const { streamType } = fromStreamName(streamName);\n\n const collection = await this.storage.collectionFor(streamType);\n\n const filter = {\n streamName: { $eq: streamName },\n };\n\n const count = await collection.countDocuments(filter, {\n useBigInt64: true,\n limit: 1,\n });\n\n return Boolean(count > 0);\n }\n\n collectionFor = async <EventType extends Event>(\n streamType: StreamType,\n ): Promise<Collection<EventStream<EventType>>> => {\n return this.storage.collectionFor(streamType);\n };\n\n /**\n * Gracefully cleans up managed resources by the MongoDBEventStore.\n * It closes MongoDB client created for the provided connection string\n * through event store options.\n *\n * @memberof Closeable\n */\n close = (): Promise<void> => {\n if (this.isClosed) return Promise.resolve();\n\n this.isClosed = true;\n if (!this.shouldManageClientLifetime) return Promise.resolve();\n\n return this.client.close();\n };\n\n private async findOneInlineProjection<Doc extends Document>(\n streamFilter: SingleProjectionQueryStreamFilter<StreamType>,\n projectionQuery?: Filter<MongoDBReadModel<Doc>>,\n ) {\n const { projectionName, streamName, streamType } =\n parseSingleProjectionQueryStreamFilter(streamFilter);\n const collection = await this.storage.collectionFor(streamType);\n const query = prependMongoFilterWithProjectionPrefix<\n Filter<MongoDBReadModel<Doc>> | undefined,\n Filter<EventStream> | undefined\n >(projectionQuery, `projections.${projectionName}`);\n\n const filters: Filter<EventStream>[] = [\n { [`projections.${projectionName}`]: { $exists: true } },\n ];\n\n if (query) {\n filters.push(query);\n }\n\n if (streamName) {\n filters.push({ streamName: { $eq: streamName } });\n }\n\n const result = await collection.findOne<{\n projections: Record<typeof projectionName, MongoDBReadModel<Doc>>;\n }>(\n { $and: filters },\n {\n useBigInt64: true,\n projection: { [`projections.${projectionName}`]: 1 },\n },\n );\n\n return result?.projections?.[projectionName] ?? null;\n }\n\n private async findInlineProjection<Doc extends Document>(\n streamFilter: MultiProjectionQueryStreamFilter<StreamType>,\n projectionQuery?: Filter<MongoDBReadModel<Doc>>,\n queryOptions?: MultiProjectionQueryOptions,\n ) {\n const parsedStreamFilter =\n parseMultiProjectionQueryStreamFilter(streamFilter);\n if (!parsedStreamFilter) return [];\n const { projectionName, streamNames, streamType } = parsedStreamFilter;\n\n const collection = await this.storage.collectionFor(streamType);\n const prefix = `projections.${projectionName}`;\n const projectionFilter = prependMongoFilterWithProjectionPrefix<\n Filter<MongoDBReadModel<Doc>> | undefined,\n Filter<EventStream> | undefined\n >(projectionQuery, prefix);\n\n const filters: Filter<EventStream>[] = [\n { [`projections.${projectionName}`]: { $ne: null } },\n ];\n\n if (projectionFilter) {\n filters.push(projectionFilter);\n }\n\n if (streamNames) {\n filters.push({ streamName: { $in: streamNames } });\n }\n\n let query = collection.find<\n EventStream & {\n projections: Record<typeof projectionName, MongoDBReadModel<Doc>>;\n }\n >(\n { $and: filters },\n {\n useBigInt64: true,\n projection: { [`projections.${projectionName}`]: 1 },\n },\n );\n\n if (queryOptions?.skip) {\n query = query.skip(queryOptions.skip);\n }\n\n if (queryOptions?.limit) {\n query = query.limit(queryOptions.limit);\n }\n\n if (queryOptions?.sort) {\n const sort = prependMongoFilterWithProjectionPrefix<Sort>(\n queryOptions.sort,\n prefix,\n );\n query = query.sort(sort);\n }\n\n const streams = await query.toArray();\n\n return streams\n .map((s) => s.projections[projectionName])\n .filter((p): p is MongoDBReadModel<Doc> => !!p);\n }\n\n private async countInlineProjection<Doc extends Document>(\n streamFilter: MultiProjectionQueryStreamFilter<StreamType>,\n projectionQuery?: Filter<MongoDBReadModel<Doc>>,\n ) {\n const parsedStreamFilter =\n parseMultiProjectionQueryStreamFilter(streamFilter);\n if (!parsedStreamFilter) return 0;\n const { projectionName, streamNames, streamType } = parsedStreamFilter;\n\n const collection = await this.storage.collectionFor(streamType);\n const prefix = `projections.${projectionName}`;\n const projectionFilter = prependMongoFilterWithProjectionPrefix<\n Filter<MongoDBReadModel<Doc>> | undefined,\n Filter<EventStream> | undefined\n >(projectionQuery, prefix);\n\n const filters: Filter<EventStream>[] = [\n { [`projections.${projectionName}`]: { $ne: null } },\n ];\n\n if (projectionFilter) {\n filters.push(projectionFilter);\n }\n\n if (streamNames) {\n filters.push({ streamName: { $in: streamNames } });\n }\n\n const total = await collection.countDocuments({ $and: filters });\n return total;\n }\n\n private getConnectedClient = async (): Promise<MongoClient> => {\n if (!this.isClosed) await this.client.connect();\n return this.client;\n };\n}\n\nfunction parseSingleProjectionQueryStreamFilter<\n T extends StreamType = StreamType,\n>(streamFilter: SingleProjectionQueryStreamFilter<T>) {\n const projectionName =\n streamFilter.projectionName ?? MongoDBDefaultInlineProjectionName;\n\n if ('streamName' in streamFilter) {\n const { streamType } = fromStreamName(streamFilter.streamName);\n return {\n projectionName,\n streamName: streamFilter.streamName,\n streamType,\n };\n }\n\n if (streamFilter.streamId) {\n const streamName = toStreamName(\n streamFilter.streamType,\n streamFilter.streamId,\n );\n return {\n projectionName,\n streamName,\n streamType: streamFilter.streamType,\n };\n }\n\n return {\n projectionName,\n streamType: streamFilter.streamType,\n };\n}\n\nfunction parseMultiProjectionQueryStreamFilter<T extends StreamType>(\n streamFilter: MultiProjectionQueryStreamFilter<T>,\n) {\n const projectionName =\n streamFilter.projectionName ?? MongoDBDefaultInlineProjectionName;\n\n if ('streamNames' in streamFilter) {\n if (streamFilter.streamNames.length == 0) return null;\n const { streamType } = fromStreamName(streamFilter.streamNames[0]!);\n return {\n projectionName,\n streamNames: streamFilter.streamNames,\n streamType,\n };\n }\n\n if (streamFilter.streamIds && streamFilter.streamIds.length > 0) {\n const streamNames = streamFilter.streamIds.map((id) =>\n toStreamName(streamFilter.streamType, id),\n );\n return {\n projectionName,\n streamNames,\n streamType: streamFilter.streamType,\n };\n }\n\n return {\n projectionName,\n streamType: streamFilter.streamType,\n };\n}\n\n/**\n * Prepends `prefix` to all object keys that don't start with a '$'\n */\nexport function prependMongoFilterWithProjectionPrefix<T, Result = T>(\n obj: T,\n prefix: string,\n): Result {\n if (typeof obj !== 'object' || obj === null || obj === undefined) {\n return obj as unknown as Result;\n }\n\n if (Array.isArray(obj)) {\n for (let i = 0; i < obj.length; i++) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n obj[i] = prependMongoFilterWithProjectionPrefix(obj[i], prefix);\n }\n return obj as unknown as Result;\n }\n\n for (const key in obj) {\n // @ts-expect-error we're forcing `k` to be a key of `T`\n const k: keyof typeof obj = addProjectionPrefixToMongoKey(key, prefix);\n if (k !== key) {\n obj[k] = obj[key as keyof typeof obj];\n delete obj[key as keyof typeof obj];\n }\n\n obj[k] = prependMongoFilterWithProjectionPrefix(obj[k], prefix);\n }\n\n return obj as unknown as Result;\n}\n\nfunction addProjectionPrefixToMongoKey(key: string, prefix: string): string {\n // MongoDB operators\n if (key[0] === '$') {\n return key;\n }\n\n return `${prefix}${key.length > 0 ? '.' : ''}${key}`;\n}\n\nexport function getMongoDBEventStore(\n options: MongoDBEventStoreOptions & { client: MongoClient },\n): MongoDBEventStore;\n\nexport function getMongoDBEventStore(\n options: MongoDBEventStoreOptions & { connectionString: string },\n): MongoDBEventStore & Closeable;\n\n// Implementation signature covers both, using a union for `options`\nexport function getMongoDBEventStore(\n options: MongoDBEventStoreOptions,\n): MongoDBEventStore | Closeable {\n const impl = new MongoDBEventStoreImplementation(options);\n\n // If a client is provided externally, we don't want to allow closing it\n if ('client' in options && 'close' in impl) {\n delete (impl as Partial<MongoDBEventStoreImplementation>).close;\n }\n\n return impl;\n}\n\n/**\n * Accepts a `streamType` (the type/category of the event stream) and an `streamId`\n * (the individual entity/object or aggregate ID) and combines them to a singular\n * `streamName` which can be used in `EventStore`.\n */\nexport function toStreamName<T extends StreamType>(\n streamType: T,\n streamId: string,\n): StreamName<T> {\n return `${streamType}:${streamId}`;\n}\n\n/**\n * Accepts a fully formatted `streamName` and returns the broken down\n * `streamType` and `streamId`.\n */\nexport function fromStreamName<T extends StreamType>(\n streamName: StreamName<T>,\n): StreamNameParts<T> {\n const parts = streamName.split(':') as [T, string];\n return {\n streamType: parts[0],\n streamId: parts[1],\n };\n}\n\n/**\n * Accepts a `streamType` (the type/category of the event stream)\n * and combines them to a `collectionName` which can be used in `EventStore`.\n */\nexport function toStreamCollectionName<T extends StreamType>(\n streamType: T,\n): StreamCollectionName<T> {\n return `emt:${streamType}`;\n}\n\n/**\n * Accepts a fully formatted `streamCollectionName` and returns the parsed `streamType`.\n */\nexport function fromStreamCollectionName<T extends StreamType>(\n streamCollectionName: StreamCollectionName<T>,\n): StreamCollectionNameParts<T> {\n const parts = streamCollectionName.split(':') as [string, T];\n return {\n streamType: parts[1],\n };\n}\n"],"mappings":"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"}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["uuid"],"sources":["../src/eventStore/projections/mongoDBInlineProjection.ts","../src/eventStore/projections/mongoDBInlineProjectionSpec.ts","../src/eventStore/storage/mongoDBEventStoreStorage.ts","../src/eventStore/mongoDBEventStore.ts"],"sourcesContent":["import type {\n CanHandle,\n Event,\n ProjectionDefinition,\n ProjectionHandler,\n ReadEvent,\n} from '@event-driven-io/emmett';\nimport type { Collection, Document, UpdateFilter } from 'mongodb';\nimport type {\n EventStream,\n MongoDBReadEventMetadata,\n MongoDBReadModel,\n MongoDBReadModelMetadata,\n} from '../mongoDBEventStore';\n\nexport const MongoDBDefaultInlineProjectionName = '_default';\n\nexport type MongoDBProjectionInlineHandlerContext<\n EventType extends Event = Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n> = {\n document: MongoDBReadModel | null;\n streamId: string;\n updates: UpdateFilter<EventStream<EventType, EventMetaDataType>>;\n collection: Collection<EventStream<EventType, EventMetaDataType>>;\n};\n\nexport type MongoDBInlineProjectionHandler<\n EventType extends Event = Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n> = ProjectionHandler<\n EventType,\n EventMetaDataType,\n MongoDBProjectionInlineHandlerContext\n>;\n\nexport type MongoDBInlineProjectionDefinition<\n EventType extends Event = Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n> = ProjectionDefinition<\n EventType,\n EventMetaDataType,\n MongoDBProjectionInlineHandlerContext\n> & { name: string };\n\nexport type InlineProjectionHandlerOptions<\n EventType extends Event = Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n> = {\n readModels: Record<string, MongoDBReadModel>;\n events: Array<ReadEvent<EventType, EventMetaDataType>>;\n projections: MongoDBInlineProjectionDefinition<\n EventType,\n EventMetaDataType\n >[];\n streamId: string;\n collection: Collection<EventStream>;\n updates: UpdateFilter<EventStream<Event>>;\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n client: {\n //todo: add client here\n };\n};\n\nexport const handleInlineProjections = async <\n EventType extends Event = Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n>(\n options: InlineProjectionHandlerOptions<EventType, EventMetaDataType>,\n): Promise<void> => {\n const {\n events,\n projections: allProjections,\n updates: update,\n streamId,\n collection,\n readModels,\n } = options;\n\n const eventTypes = events.map((e) => e.type);\n\n const projections = allProjections.filter((p) =>\n p.canHandle.some((type) => eventTypes.includes(type)),\n );\n\n for (const projection of projections) {\n await projection.handle(events, {\n document: readModels[projection.name] ?? null,\n streamId,\n collection,\n updates: update,\n });\n }\n};\n\nexport type MongoDBWithNotNullDocumentEvolve<\n Doc extends Document,\n EventType extends Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n> =\n | ((\n document: Doc,\n event: ReadEvent<EventType, EventMetaDataType>,\n ) => Doc | null)\n | ((document: Doc, event: ReadEvent<EventType>) => Promise<Doc | null>);\n\nexport type MongoDBWithNullableDocumentEvolve<\n Doc extends Document,\n EventType extends Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n> =\n | ((\n document: Doc | null,\n event: ReadEvent<EventType, EventMetaDataType>,\n ) => Doc | null)\n | ((\n document: Doc | null,\n event: ReadEvent<EventType>,\n ) => Promise<Doc | null>);\n\nexport type MongoDBInlineProjectionOptions<\n Doc extends Document,\n EventType extends Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n> = {\n name?: string;\n schemaVersion?: number;\n canHandle: CanHandle<EventType>;\n} & (\n | {\n evolve: MongoDBWithNullableDocumentEvolve<\n Doc,\n EventType,\n EventMetaDataType\n >;\n }\n | {\n evolve: MongoDBWithNotNullDocumentEvolve<\n Doc,\n EventType,\n EventMetaDataType\n >;\n initialState: () => Doc;\n }\n);\n\nexport const mongoDBInlineProjection = <\n Doc extends Document,\n EventType extends Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n>(\n options: MongoDBInlineProjectionOptions<Doc, EventType, EventMetaDataType>,\n): MongoDBInlineProjectionDefinition => {\n const projectionName = options.name ?? MongoDBDefaultInlineProjectionName;\n const schemaVersion = options.schemaVersion ?? 1;\n\n return {\n name: projectionName,\n canHandle: options.canHandle,\n handle: async (events, { document, updates, streamId }) => {\n if (events.length === 0) return;\n\n let state =\n 'initialState' in options\n ? (document ?? options.initialState())\n : document;\n\n for (const event of events) {\n state = await options.evolve(\n state as Doc,\n event as ReadEvent<EventType, EventMetaDataType>,\n );\n }\n\n const metadata: MongoDBReadModelMetadata = {\n streamId,\n name: projectionName,\n schemaVersion,\n streamPosition: events[events.length - 1]!.metadata.streamPosition,\n };\n\n updates.$set![`projections.${projectionName}`] =\n state !== null\n ? {\n ...state,\n _metadata: metadata,\n }\n : null;\n },\n };\n};\n","import {\n assertFails,\n AssertionError,\n assertTrue,\n deepEquals,\n isErrorConstructor,\n isSubset,\n projections,\n type Event,\n type ThenThrows,\n} from '@event-driven-io/emmett';\nimport { MongoClient, type Document } from 'mongodb';\nimport {\n getMongoDBEventStore,\n type MongoDBEventStore,\n type MongoDBEventStoreConnectionOptions,\n type MongoDBReadModel,\n type StreamName,\n} from '../mongoDBEventStore';\nimport {\n MongoDBDefaultInlineProjectionName,\n type MongoDBInlineProjectionDefinition,\n} from './mongoDBInlineProjection';\n\nexport type MongoDBInlineProjectionSpecGivenEvents<\n StreamNameType extends StreamName,\n EventType extends Event,\n> = {\n streamName: StreamNameType;\n events: EventType[];\n};\n\nexport type MongoDBInlineProjectionSpec<\n StreamNameType extends StreamName,\n EventType extends Event,\n> = (\n givenStream: MongoDBInlineProjectionSpecGivenEvents<\n StreamNameType,\n EventType\n >,\n) => {\n when: (events: EventType[]) => {\n then: (\n assert: MongoDBInlineProjectionAssert,\n message?: string,\n ) => Promise<void>;\n thenThrows: <ErrorType extends Error = Error>(\n ...args: Parameters<ThenThrows<ErrorType>>\n ) => Promise<void>;\n };\n};\n\nexport type MongoDBInlineProjectionAssertOptions<\n StreamNameType extends StreamName = StreamName,\n> = {\n streamName: StreamNameType;\n eventStore: MongoDBEventStore;\n};\n\nexport type MongoDBInlineProjectionAssert<\n StreamNameType extends StreamName = StreamName,\n> = (\n options: MongoDBInlineProjectionAssertOptions<StreamNameType>,\n) => Promise<void | boolean>;\n\nexport type MongoDBInlineProjectionSpecOptions = {\n projection: MongoDBInlineProjectionDefinition;\n} & MongoDBEventStoreConnectionOptions;\n\nexport const MongoDBInlineProjectionSpec = {\n for: <StreamNameType extends StreamName, EventType extends Event>(\n options: MongoDBInlineProjectionSpecOptions,\n ): MongoDBInlineProjectionSpec<StreamNameType, EventType> => {\n {\n const { projection, ...connectionOptions } = options;\n\n return (\n givenStream: MongoDBInlineProjectionSpecGivenEvents<\n StreamNameType,\n EventType\n >,\n ) => {\n const { streamName, events: givenEvents } = givenStream;\n return {\n when: (events: EventType[]) => {\n const allEvents = [...givenEvents, ...events];\n\n const run = (eventStore: MongoDBEventStore) =>\n eventStore.appendToStream(streamName, allEvents);\n\n return {\n then: async (\n assert: MongoDBInlineProjectionAssert,\n message?: string,\n ): Promise<void> => {\n const client =\n 'client' in connectionOptions && connectionOptions.client\n ? connectionOptions.client\n : new MongoClient(\n connectionOptions.connectionString,\n connectionOptions.clientOptions,\n );\n\n const eventStore = getMongoDBEventStore({\n projections: projections.inline([projection]),\n client,\n });\n\n try {\n await run(eventStore);\n\n const succeeded = await assert({ eventStore, streamName });\n\n if (succeeded !== undefined && succeeded === false)\n assertFails(\n message ??\n \"Projection specification didn't match the criteria\",\n );\n } finally {\n await client.close();\n }\n },\n thenThrows: async <ErrorType extends Error>(\n ...args: Parameters<ThenThrows<ErrorType>>\n ): Promise<void> => {\n const client =\n 'client' in connectionOptions && connectionOptions.client\n ? connectionOptions.client\n : new MongoClient(\n connectionOptions.connectionString,\n connectionOptions.clientOptions,\n );\n\n const eventStore = getMongoDBEventStore({\n projections: projections.inline([projection]),\n client,\n });\n\n try {\n await run(eventStore);\n throw new AssertionError('Handler did not fail as expected');\n } catch (error) {\n if (error instanceof AssertionError) throw error;\n\n if (args.length === 0) return;\n\n if (!isErrorConstructor(args[0])) {\n assertTrue(\n args[0](error as ErrorType),\n `Error didn't match the error condition: ${error?.toString()}`,\n );\n return;\n }\n\n assertTrue(\n error instanceof args[0],\n `Caught error is not an instance of the expected type: ${error?.toString()}`,\n );\n\n if (args[1]) {\n assertTrue(\n args[1](error as ErrorType),\n `Error didn't match the error condition: ${error?.toString()}`,\n );\n }\n } finally {\n await client.close();\n }\n },\n };\n },\n };\n };\n }\n },\n};\n\nexport const eventInStream = <\n StreamNameType extends StreamName,\n EventType extends Event,\n>(\n streamName: StreamNameType,\n event: EventType,\n): MongoDBInlineProjectionSpecGivenEvents<StreamNameType, EventType> => ({\n streamName,\n events: [event],\n});\n\nexport const eventsInStream = <\n StreamNameType extends StreamName,\n EventType extends Event,\n>(\n streamName: StreamNameType,\n events: EventType[],\n): MongoDBInlineProjectionSpecGivenEvents<StreamNameType, EventType> => ({\n streamName,\n events,\n});\n\nconst expectReadModelToMatch = async <\n Doc extends Document = Document,\n StreamNameType extends StreamName = StreamName,\n>(\n options: MongoDBInlineProjectionAssertOptions<StreamNameType> & {\n projectionName: string;\n match: (readModel: MongoDBReadModel<Doc> | null) => boolean;\n },\n) => {\n const { streamName, projectionName, eventStore, match } = options;\n const readModel = await eventStore.projections.inline.findOne<Doc>({\n streamName,\n projectionName,\n });\n\n return match(readModel);\n};\n\nconst expectInlineReadModelWithName = (projectionName: string) => ({\n toHave:\n <Doc extends Document, StreamNameType extends StreamName = StreamName>(\n expected: Partial<MongoDBReadModel<Doc>> | null,\n ): MongoDBInlineProjectionAssert<StreamNameType> =>\n ({ eventStore, streamName }) =>\n expectReadModelToMatch<Doc>({\n eventStore,\n streamName,\n projectionName,\n match: (readModel) => isSubset(readModel, expected),\n }),\n toDeepEquals:\n <Doc extends Document, StreamNameType extends StreamName = StreamName>(\n expected: MongoDBReadModel<Doc> | null,\n ): MongoDBInlineProjectionAssert<StreamNameType> =>\n ({ eventStore, streamName }) =>\n expectReadModelToMatch<Doc>({\n eventStore,\n streamName,\n projectionName,\n match: (readModel) => deepEquals(readModel, expected),\n }),\n toMatch:\n <Doc extends Document, StreamNameType extends StreamName = StreamName>(\n match: (readModel: MongoDBReadModel<Doc> | null) => boolean,\n ): MongoDBInlineProjectionAssert<StreamNameType> =>\n ({ eventStore, streamName }) =>\n expectReadModelToMatch<Doc>({\n eventStore,\n streamName,\n projectionName,\n match,\n }),\n notToExist:\n <\n StreamNameType extends StreamName = StreamName,\n >(): MongoDBInlineProjectionAssert<StreamNameType> =>\n ({ eventStore, streamName }) =>\n expectReadModelToMatch({\n eventStore,\n streamName,\n projectionName,\n match: (readModel) => readModel === null,\n }),\n toExist:\n (): MongoDBInlineProjectionAssert =>\n ({ eventStore, streamName }) =>\n expectReadModelToMatch({\n eventStore,\n streamName,\n projectionName,\n match: (readModel) => readModel !== null,\n }),\n});\n\nexport const expectInlineReadModel = {\n withName: (name: string) => expectInlineReadModelWithName(name),\n ...expectInlineReadModelWithName(MongoDBDefaultInlineProjectionName),\n};\n","import type { Event } from '@event-driven-io/emmett';\nimport type { Collection, Db, MongoClient } from 'mongodb';\nimport {\n toStreamCollectionName,\n type EventStream,\n type StreamType,\n} from '../mongoDBEventStore';\n\nexport type MongoDBEventStoreCollectionPerStreamTypeStorageOptions = {\n /**\n * The recommended setting where each stream type will be kept\n * in a separate collection type using the format: `emt_${streamType}`.\n */\n type: 'COLLECTION_PER_STREAM_TYPE';\n databaseName?: string;\n};\n\nexport type MongoDBEventStoreSingleCollectionStorageOptions = {\n /**\n * All streams will be kept withing a single MongDB collection\n * It'll either use default collection name (\"emt_streams\")\n * or provided name through 'collection' param.\n */\n type: 'SINGLE_COLLECTION';\n collectionName?: string;\n databaseName?: string;\n};\n\nexport type MongoDBEventStoreCollectionResolution = {\n databaseName?: string;\n collectionName: string;\n};\n\nexport type MongoDBEventStoreCustomStorageOptions = {\n /**\n * This is advanced option, where you specify your own collection\n * resolution function. You can do that by specifying the `collectionFor` function.\n */\n type: 'CUSTOM';\n databaseName?: string;\n collectionFor: <T extends StreamType>(\n streamType: T,\n ) => string | MongoDBEventStoreCollectionResolution;\n};\n\nexport type MongoDBEventStoreStorageOptions =\n | 'COLLECTION_PER_STREAM_TYPE'\n | 'SINGLE_COLLECTION'\n | MongoDBEventStoreSingleCollectionStorageOptions\n | MongoDBEventStoreCollectionPerStreamTypeStorageOptions\n | MongoDBEventStoreCustomStorageOptions;\n\nexport const DefaultMongoDBEventStoreStorageOptions =\n 'COLLECTION_PER_STREAM_TYPE';\n\nexport type MongoDBEventStoreStorage = {\n collectionFor: <T extends StreamType, EventType extends Event = Event>(\n streamType: T,\n ) => Promise<Collection<EventStream<EventType>>>;\n};\n\nexport const DefaultMongoDBEventStoreCollectionName = 'emt:streams';\n\nconst resolveCollectionAndDatabase = <T extends StreamType>(\n streamType: T,\n options: MongoDBEventStoreStorageOptions,\n): MongoDBEventStoreCollectionResolution => {\n if (\n options === 'SINGLE_COLLECTION' ||\n (typeof options === 'object' && options.type === 'SINGLE_COLLECTION')\n ) {\n return {\n collectionName:\n typeof options === 'object'\n ? (options.collectionName ?? DefaultMongoDBEventStoreCollectionName)\n : DefaultMongoDBEventStoreCollectionName,\n databaseName:\n typeof options === 'object' ? options.databaseName : undefined,\n };\n } else if (\n options === 'COLLECTION_PER_STREAM_TYPE' ||\n (typeof options === 'object' &&\n options.type === 'COLLECTION_PER_STREAM_TYPE')\n ) {\n return {\n collectionName: toStreamCollectionName(streamType),\n databaseName:\n typeof options === 'object' ? options.databaseName : undefined,\n };\n } else {\n const result = options.collectionFor(streamType);\n return {\n collectionName:\n typeof result === 'object' ? result.collectionName : result,\n databaseName:\n typeof result === 'object'\n ? (result.databaseName ?? options.databaseName)\n : options.databaseName,\n };\n }\n};\n\nconst getDB = async (options: {\n databaseName: string | undefined;\n dbsCache: Map<string, Db>;\n getConnectedClient: () => Promise<MongoClient>;\n}): Promise<Db> => {\n const { dbsCache, databaseName, getConnectedClient } = options;\n const safeDbName = databaseName ?? '___default';\n\n let db = dbsCache.get(safeDbName);\n\n if (!db) {\n const connectedClient = await getConnectedClient();\n\n db = connectedClient.db(databaseName);\n\n dbsCache.set(safeDbName, db);\n }\n\n return db;\n};\n\nconst collectionFor = async <EventType extends Event = Event>(options: {\n collectionName: string;\n streamCollections: Map<string, Collection<EventStream>>;\n db: Db;\n}): Promise<Collection<EventStream<EventType>>> => {\n const { collectionName, db, streamCollections } = options;\n\n let collection = streamCollections.get(collectionName) as\n | Collection<EventStream<EventType>>\n | undefined;\n\n if (!collection) {\n collection = db.collection<EventStream<EventType>>(collectionName);\n await collection.createIndex({ streamName: 1 }, { unique: true });\n\n streamCollections.set(\n collectionName,\n collection as Collection<EventStream>,\n );\n }\n\n return collection;\n};\n\nexport const mongoDBEventStoreStorage = (options: {\n storage?: MongoDBEventStoreStorageOptions | undefined;\n getConnectedClient: () => Promise<MongoClient>;\n}): MongoDBEventStoreStorage => {\n const dbsCache: Map<string, Db> = new Map();\n const streamCollections: Map<string, Collection<EventStream>> = new Map();\n const storageOptions =\n options.storage ?? DefaultMongoDBEventStoreStorageOptions;\n\n const { getConnectedClient } = options;\n\n return {\n collectionFor: async <\n T extends StreamType,\n EventType extends Event = Event,\n >(\n streamType: T,\n ): Promise<Collection<EventStream<EventType>>> => {\n const { collectionName, databaseName } = resolveCollectionAndDatabase(\n streamType,\n storageOptions,\n );\n\n let collection = streamCollections.get(collectionName) as\n | Collection<EventStream<EventType>>\n | undefined;\n\n if (!collection) {\n const db = await getDB({ databaseName, dbsCache, getConnectedClient });\n collection = await collectionFor<EventType>({\n collectionName,\n streamCollections,\n db,\n });\n }\n\n return collection;\n },\n };\n};\n","import {\n assertExpectedVersionMatchesCurrent,\n downcastRecordedMessage,\n ExpectedVersionConflictError,\n filterProjections,\n tryPublishMessagesAfterCommit,\n upcastRecordedMessages,\n type AggregateStreamOptions,\n type AggregateStreamResult,\n type AppendToStreamOptions,\n type AppendToStreamResult,\n type Closeable,\n type DefaultEventStoreOptions,\n type Event,\n type EventStore,\n type ProjectionRegistration,\n type ReadEvent,\n type ReadEventMetadataWithoutGlobalPosition,\n type ReadStreamOptions,\n type ReadStreamResult,\n type StreamExistsResult,\n} from '@event-driven-io/emmett';\nimport {\n MongoClient,\n type Collection,\n type Document,\n type Filter,\n type MongoClientOptions,\n type Sort,\n type UpdateFilter,\n type WithId,\n} from 'mongodb';\nimport { v4 as uuid } from 'uuid';\nimport {\n handleInlineProjections,\n MongoDBDefaultInlineProjectionName,\n type MongoDBInlineProjectionDefinition,\n type MongoDBProjectionInlineHandlerContext,\n} from './projections';\nimport {\n mongoDBEventStoreStorage,\n type MongoDBEventStoreStorage,\n type MongoDBEventStoreStorageOptions,\n} from './storage';\n\nexport const MongoDBEventStoreDefaultStreamVersion = 0n;\n\nexport type StreamType = string;\nexport type StreamName<T extends StreamType = StreamType> = `${T}:${string}`;\n\nexport type StreamNameParts<T extends StreamType = StreamType> = {\n streamType: T;\n streamId: string;\n};\n\nexport type StreamCollectionName<T extends StreamType = StreamType> =\n `emt:${T}`;\n\nexport type StreamCollectionNameParts<T extends StreamType = StreamType> = {\n streamType: T;\n};\n\nexport type MongoDBReadModelMetadata = {\n streamId: string;\n name: string;\n schemaVersion: number;\n streamPosition: bigint;\n};\n\nexport type MongoDBReadModel<Doc extends Document = Document> = Doc & {\n _metadata: MongoDBReadModelMetadata;\n};\n\nexport interface EventStream<\n EventType extends Event = Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n> {\n streamName: string;\n messages: Array<ReadEvent<EventType, EventMetaDataType>>;\n metadata: {\n streamId: string;\n streamType: StreamType;\n streamPosition: bigint;\n createdAt: Date;\n updatedAt: Date;\n };\n projections: Record<string, MongoDBReadModel>;\n}\n\nexport type MongoDBReadEventMetadata = ReadEventMetadataWithoutGlobalPosition;\n\nexport type MongoDBReadEvent<EventType extends Event = Event> = ReadEvent<\n EventType,\n MongoDBReadEventMetadata\n>;\n\ntype SingleProjectionQueryStreamFilter<T extends StreamType> = {\n projectionName?: string;\n} & ({ streamName: StreamName<T> } | { streamType: T; streamId?: string });\n\ntype MultiProjectionQueryStreamFilter<T extends StreamType> = {\n projectionName?: string;\n} & (\n | { streamNames: StreamName<T>[] }\n | { streamType: T; streamIds?: string[] }\n);\n\ntype MultiProjectionQueryOptions = {\n skip?: number;\n limit?: number;\n sort?: [string, 1 | -1][] | Record<string, 1 | -1>;\n};\n\n/**\n * Helpers for querying inline projections on event streams.\n */\ntype InlineProjectionQueries<T extends StreamType> = {\n /**\n * Helper for querying for a single projection. Similar to `collection.findOne`.\n * @param streamFilter - A filter object for stream level fields. If `streamType` is required if `streamName` is not provided. If `projectionName` is not provided, the default projection will be used (`MongoDBDefaultInlineProjectionName`).\n * @param projectionQuery - A MongoDB filter query based on the projection schema. Internally, this function will prepend each object key with the necessary projection name.\n */\n findOne: <Doc extends Document>(\n streamFilter: SingleProjectionQueryStreamFilter<T>,\n projectionQuery?: Filter<MongoDBReadModel<Doc>>,\n ) => Promise<MongoDBReadModel<Doc> | null>;\n /**\n * Helper for querying for multiple projections. Similar to `collection.find`.\n *\n * ***NOTE***: If `streamFilter.streamNames` is an empty array, this function will return an empty array. If `streamFilter.streamIds` is an empty array, the `streamIds` filter will not be used.\n *\n * @param streamFilter - A filter object for stream level fields. If `streamType` is required if `streamNames` is not provided. If `projectionName` is not provided, the default projection will be used (`MongoDBDefaultInlineProjectionName`).\n * @param projectionQuery - A MongoDB filter query based on the projection schema. Internally, this function will prepend each object key with the necessary projection name.\n * @param queryOptions - Additional query options like `skip`, `limit`, and `sort`. `sort`, similar to `projectionQuery`, will prepend each object key with the necessary projection name.\n */\n find: <Doc extends Document>(\n streamFilter: MultiProjectionQueryStreamFilter<T>,\n projectionQuery?: Filter<MongoDBReadModel<Doc>>,\n queryOptions?: MultiProjectionQueryOptions,\n ) => Promise<MongoDBReadModel<Doc>[]>;\n /**\n * Returns the total number of documents matching the provided filter options. Similar to `collection.countDocuments`.\n *\n * ***NOTE***: If `streamFilter.streamNames` is an empty array, this function will return `0`. If `streamFilter.streamIds` is an empty array, the `streamIds` filter will not be used.\n *\n * @param streamFilter - A filter object for stream level fields. If `streamType` is required if `streamNames` is not provided. If `projectionName` is not provided, the default projection will be used (`MongoDBDefaultInlineProjectionName`).\n * @param projectionQuery - A MongoDB filter query based on the projection schema. Internally, this function will prepend each object key with the necessary projection name.\n */\n count: <Doc extends Document>(\n streamFilter: MultiProjectionQueryStreamFilter<T>,\n projectionQuery?: Filter<MongoDBReadModel<Doc>>,\n ) => Promise<number>;\n};\n\n/**\n * Helpers for querying projections on event streams.\n */\ntype ProjectionQueries<T extends StreamType> = {\n inline: InlineProjectionQueries<T>;\n};\n\nexport type MongoDBEventStoreClientOptions = {\n client: MongoClient;\n connectionString?: never;\n clientOptions?: never;\n};\n\nexport type MongoDBEventStoreConnectionStringOptions = {\n client?: never;\n connectionString: string;\n clientOptions?: MongoClientOptions;\n};\n\nexport type MongoDBEventStoreConnectionOptions =\n | MongoDBEventStoreClientOptions\n | MongoDBEventStoreConnectionStringOptions;\n\nexport type MongoDBEventStoreOptions = {\n projections?: ProjectionRegistration<\n 'inline',\n MongoDBReadEventMetadata,\n MongoDBProjectionInlineHandlerContext\n >[];\n storage?: MongoDBEventStoreStorageOptions;\n} & MongoDBEventStoreConnectionOptions &\n DefaultEventStoreOptions<MongoDBEventStore>;\n\nexport type MongoDBEventStore = EventStore<MongoDBReadEventMetadata> & {\n projections: ProjectionQueries<StreamType>;\n collectionFor: <EventType extends Event>(\n streamType: StreamType,\n ) => Promise<Collection<EventStream<EventType>>>;\n};\n\nclass MongoDBEventStoreImplementation implements MongoDBEventStore, Closeable {\n private readonly client: MongoClient;\n private readonly inlineProjections: MongoDBInlineProjectionDefinition[];\n private shouldManageClientLifetime: boolean;\n private isClosed: boolean = false;\n private storage: MongoDBEventStoreStorage;\n private options: MongoDBEventStoreOptions;\n public projections: ProjectionQueries<StreamType>;\n\n constructor(options: MongoDBEventStoreOptions) {\n this.options = options;\n this.client =\n 'client' in options && options.client\n ? options.client\n : new MongoClient(options.connectionString, options.clientOptions);\n this.shouldManageClientLifetime = !('client' in options);\n this.storage = mongoDBEventStoreStorage({\n storage: options.storage,\n getConnectedClient: () => this.getConnectedClient(),\n });\n this.inlineProjections = filterProjections(\n 'inline',\n options.projections ?? [],\n ) as MongoDBInlineProjectionDefinition[];\n\n this.projections = {\n inline: {\n findOne: this.findOneInlineProjection.bind(this),\n find: this.findInlineProjection.bind(this),\n count: this.countInlineProjection.bind(this),\n },\n };\n }\n\n async readStream<\n EventType extends Event,\n EventPayloadType extends Event = EventType,\n >(\n streamName: StreamName,\n options?: ReadStreamOptions<EventType, EventPayloadType>,\n ): Promise<\n Exclude<ReadStreamResult<EventType, MongoDBReadEventMetadata>, null>\n > {\n const { streamType } = fromStreamName(streamName);\n const expectedStreamVersion = options?.expectedStreamVersion;\n\n const collection = await this.storage.collectionFor(streamType);\n\n const filter = {\n streamName: { $eq: streamName },\n };\n\n const eventsSliceArr: number[] = [];\n\n if (options && 'from' in options) {\n eventsSliceArr.push(Number(options.from));\n } else {\n eventsSliceArr.push(0);\n }\n\n if (options && 'to' in options) {\n eventsSliceArr.push(Number(options.to));\n }\n\n const eventsSlice =\n eventsSliceArr.length > 1 ? { $slice: eventsSliceArr } : 1;\n\n const stream = await collection.findOne<\n WithId<Pick<EventStream<EventPayloadType>, 'metadata' | 'messages'>>\n >(filter, {\n useBigInt64: true,\n projection: {\n metadata: 1,\n messages: eventsSlice,\n },\n });\n\n if (!stream) {\n return {\n events: [],\n currentStreamVersion: MongoDBEventStoreDefaultStreamVersion,\n streamExists: false,\n };\n }\n\n assertExpectedVersionMatchesCurrent(\n stream.metadata.streamPosition,\n expectedStreamVersion,\n MongoDBEventStoreDefaultStreamVersion,\n );\n\n const events = upcastRecordedMessages(\n stream.messages,\n options?.schema?.versioning,\n );\n\n return {\n events,\n currentStreamVersion: stream.metadata.streamPosition,\n streamExists: true,\n };\n }\n\n async aggregateStream<\n State,\n EventType extends Event,\n EventPayloadType extends Event = EventType,\n >(\n streamName: StreamName,\n options: AggregateStreamOptions<\n State,\n EventType,\n MongoDBReadEventMetadata,\n EventPayloadType\n >,\n ): Promise<AggregateStreamResult<State>> {\n const stream = await this.readStream<EventType, EventPayloadType>(\n streamName,\n options?.read,\n );\n const { evolve, initialState } = options;\n\n const state = stream.events.reduce(evolve, initialState());\n return {\n state,\n currentStreamVersion: stream.currentStreamVersion,\n streamExists: stream.streamExists,\n };\n }\n\n async appendToStream<\n EventType extends Event,\n EventPayloadType extends Event = EventType,\n >(\n streamName: StreamName,\n events: EventType[],\n options?: AppendToStreamOptions<EventType, EventPayloadType>,\n ): Promise<AppendToStreamResult> {\n const { streamId, streamType } = fromStreamName(streamName);\n const expectedStreamVersion = options?.expectedStreamVersion;\n\n const collection = await this.storage.collectionFor(streamType);\n\n const stream = await collection.findOne<\n WithId<Pick<EventStream<EventPayloadType>, 'metadata' | 'projections'>>\n >(\n { streamName: { $eq: streamName } },\n {\n useBigInt64: true,\n projection: {\n 'metadata.streamPosition': 1,\n projections: 1,\n },\n },\n );\n\n const currentStreamVersion =\n stream?.metadata.streamPosition ?? MongoDBEventStoreDefaultStreamVersion;\n\n assertExpectedVersionMatchesCurrent(\n currentStreamVersion,\n expectedStreamVersion,\n MongoDBEventStoreDefaultStreamVersion,\n );\n\n let streamOffset = currentStreamVersion;\n\n const eventsToAppend: ReadEvent<\n EventPayloadType,\n MongoDBReadEventMetadata\n >[] = events.map((event) => {\n const metadata: MongoDBReadEventMetadata = {\n messageId: uuid(),\n streamName,\n streamPosition: ++streamOffset,\n };\n return downcastRecordedMessage(\n {\n type: event.type,\n data: event.data,\n metadata: {\n ...metadata,\n ...('metadata' in event ? (event.metadata ?? {}) : {}),\n },\n } as ReadEvent<EventType, MongoDBReadEventMetadata>,\n options?.schema?.versioning,\n );\n });\n\n const now = new Date();\n const updates: UpdateFilter<EventStream> = {\n $push: { messages: { $each: eventsToAppend } },\n $set: {\n 'metadata.updatedAt': now,\n 'metadata.streamPosition': currentStreamVersion + BigInt(events.length),\n },\n $setOnInsert: {\n streamName,\n 'metadata.streamId': streamId,\n 'metadata.streamType': streamType,\n 'metadata.createdAt': now,\n },\n };\n\n if (this.inlineProjections) {\n await handleInlineProjections({\n readModels: stream?.projections ?? {},\n streamId,\n events: eventsToAppend,\n projections: this.inlineProjections,\n collection,\n updates,\n client: {},\n });\n }\n\n const updatedStream = await collection.updateOne(\n {\n streamName: { $eq: streamName },\n 'metadata.streamPosition': currentStreamVersion,\n },\n updates,\n { useBigInt64: true, upsert: true },\n );\n\n if (!updatedStream) {\n throw new ExpectedVersionConflictError(\n currentStreamVersion,\n options?.expectedStreamVersion ?? 0n,\n );\n }\n\n await tryPublishMessagesAfterCommit<MongoDBEventStore>(\n eventsToAppend,\n this.options.hooks,\n // {\n // TODO: same context as InlineProjectionHandlerContext for mongodb?\n // },\n );\n\n return {\n nextExpectedStreamVersion:\n currentStreamVersion + BigInt(eventsToAppend.length),\n createdNewStream:\n currentStreamVersion === MongoDBEventStoreDefaultStreamVersion,\n };\n }\n\n async streamExists(streamName: StreamName): Promise<StreamExistsResult> {\n const { streamType } = fromStreamName(streamName);\n\n const collection = await this.storage.collectionFor(streamType);\n\n const filter = {\n streamName: { $eq: streamName },\n };\n\n const count = await collection.countDocuments(filter, {\n useBigInt64: true,\n limit: 1,\n });\n\n return Boolean(count > 0);\n }\n\n collectionFor = async <EventType extends Event>(\n streamType: StreamType,\n ): Promise<Collection<EventStream<EventType>>> => {\n return this.storage.collectionFor(streamType);\n };\n\n /**\n * Gracefully cleans up managed resources by the MongoDBEventStore.\n * It closes MongoDB client created for the provided connection string\n * through event store options.\n *\n * @memberof Closeable\n */\n close = (): Promise<void> => {\n if (this.isClosed) return Promise.resolve();\n\n this.isClosed = true;\n if (!this.shouldManageClientLifetime) return Promise.resolve();\n\n return this.client.close();\n };\n\n private async findOneInlineProjection<Doc extends Document>(\n streamFilter: SingleProjectionQueryStreamFilter<StreamType>,\n projectionQuery?: Filter<MongoDBReadModel<Doc>>,\n ) {\n const { projectionName, streamName, streamType } =\n parseSingleProjectionQueryStreamFilter(streamFilter);\n const collection = await this.storage.collectionFor(streamType);\n const query = prependMongoFilterWithProjectionPrefix<\n Filter<MongoDBReadModel<Doc>> | undefined,\n Filter<EventStream> | undefined\n >(projectionQuery, `projections.${projectionName}`);\n\n const filters: Filter<EventStream>[] = [\n { [`projections.${projectionName}`]: { $exists: true } },\n ];\n\n if (query) {\n filters.push(query);\n }\n\n if (streamName) {\n filters.push({ streamName: { $eq: streamName } });\n }\n\n const result = await collection.findOne<{\n projections: Record<typeof projectionName, MongoDBReadModel<Doc>>;\n }>(\n { $and: filters },\n {\n useBigInt64: true,\n projection: { [`projections.${projectionName}`]: 1 },\n },\n );\n\n return result?.projections?.[projectionName] ?? null;\n }\n\n private async findInlineProjection<Doc extends Document>(\n streamFilter: MultiProjectionQueryStreamFilter<StreamType>,\n projectionQuery?: Filter<MongoDBReadModel<Doc>>,\n queryOptions?: MultiProjectionQueryOptions,\n ) {\n const parsedStreamFilter =\n parseMultiProjectionQueryStreamFilter(streamFilter);\n if (!parsedStreamFilter) return [];\n const { projectionName, streamNames, streamType } = parsedStreamFilter;\n\n const collection = await this.storage.collectionFor(streamType);\n const prefix = `projections.${projectionName}`;\n const projectionFilter = prependMongoFilterWithProjectionPrefix<\n Filter<MongoDBReadModel<Doc>> | undefined,\n Filter<EventStream> | undefined\n >(projectionQuery, prefix);\n\n const filters: Filter<EventStream>[] = [\n { [`projections.${projectionName}`]: { $ne: null } },\n ];\n\n if (projectionFilter) {\n filters.push(projectionFilter);\n }\n\n if (streamNames) {\n filters.push({ streamName: { $in: streamNames } });\n }\n\n let query = collection.find<\n EventStream & {\n projections: Record<typeof projectionName, MongoDBReadModel<Doc>>;\n }\n >(\n { $and: filters },\n {\n useBigInt64: true,\n projection: { [`projections.${projectionName}`]: 1 },\n },\n );\n\n if (queryOptions?.skip) {\n query = query.skip(queryOptions.skip);\n }\n\n if (queryOptions?.limit) {\n query = query.limit(queryOptions.limit);\n }\n\n if (queryOptions?.sort) {\n const sort = prependMongoFilterWithProjectionPrefix<Sort>(\n queryOptions.sort,\n prefix,\n );\n query = query.sort(sort);\n }\n\n const streams = await query.toArray();\n\n return streams\n .map((s) => s.projections[projectionName])\n .filter((p): p is MongoDBReadModel<Doc> => !!p);\n }\n\n private async countInlineProjection<Doc extends Document>(\n streamFilter: MultiProjectionQueryStreamFilter<StreamType>,\n projectionQuery?: Filter<MongoDBReadModel<Doc>>,\n ) {\n const parsedStreamFilter =\n parseMultiProjectionQueryStreamFilter(streamFilter);\n if (!parsedStreamFilter) return 0;\n const { projectionName, streamNames, streamType } = parsedStreamFilter;\n\n const collection = await this.storage.collectionFor(streamType);\n const prefix = `projections.${projectionName}`;\n const projectionFilter = prependMongoFilterWithProjectionPrefix<\n Filter<MongoDBReadModel<Doc>> | undefined,\n Filter<EventStream> | undefined\n >(projectionQuery, prefix);\n\n const filters: Filter<EventStream>[] = [\n { [`projections.${projectionName}`]: { $ne: null } },\n ];\n\n if (projectionFilter) {\n filters.push(projectionFilter);\n }\n\n if (streamNames) {\n filters.push({ streamName: { $in: streamNames } });\n }\n\n const total = await collection.countDocuments({ $and: filters });\n return total;\n }\n\n private getConnectedClient = async (): Promise<MongoClient> => {\n if (!this.isClosed) await this.client.connect();\n return this.client;\n };\n}\n\nfunction parseSingleProjectionQueryStreamFilter<\n T extends StreamType = StreamType,\n>(streamFilter: SingleProjectionQueryStreamFilter<T>) {\n const projectionName =\n streamFilter.projectionName ?? MongoDBDefaultInlineProjectionName;\n\n if ('streamName' in streamFilter) {\n const { streamType } = fromStreamName(streamFilter.streamName);\n return {\n projectionName,\n streamName: streamFilter.streamName,\n streamType,\n };\n }\n\n if (streamFilter.streamId) {\n const streamName = toStreamName(\n streamFilter.streamType,\n streamFilter.streamId,\n );\n return {\n projectionName,\n streamName,\n streamType: streamFilter.streamType,\n };\n }\n\n return {\n projectionName,\n streamType: streamFilter.streamType,\n };\n}\n\nfunction parseMultiProjectionQueryStreamFilter<T extends StreamType>(\n streamFilter: MultiProjectionQueryStreamFilter<T>,\n) {\n const projectionName =\n streamFilter.projectionName ?? MongoDBDefaultInlineProjectionName;\n\n if ('streamNames' in streamFilter) {\n if (streamFilter.streamNames.length == 0) return null;\n const { streamType } = fromStreamName(streamFilter.streamNames[0]!);\n return {\n projectionName,\n streamNames: streamFilter.streamNames,\n streamType,\n };\n }\n\n if (streamFilter.streamIds && streamFilter.streamIds.length > 0) {\n const streamNames = streamFilter.streamIds.map((id) =>\n toStreamName(streamFilter.streamType, id),\n );\n return {\n projectionName,\n streamNames,\n streamType: streamFilter.streamType,\n };\n }\n\n return {\n projectionName,\n streamType: streamFilter.streamType,\n };\n}\n\n/**\n * Prepends `prefix` to all object keys that don't start with a '$'\n */\nexport function prependMongoFilterWithProjectionPrefix<T, Result = T>(\n obj: T,\n prefix: string,\n): Result {\n if (typeof obj !== 'object' || obj === null || obj === undefined) {\n return obj as unknown as Result;\n }\n\n if (Array.isArray(obj)) {\n for (let i = 0; i < obj.length; i++) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n obj[i] = prependMongoFilterWithProjectionPrefix(obj[i], prefix);\n }\n return obj as unknown as Result;\n }\n\n for (const key in obj) {\n // @ts-expect-error we're forcing `k` to be a key of `T`\n const k: keyof typeof obj = addProjectionPrefixToMongoKey(key, prefix);\n if (k !== key) {\n obj[k] = obj[key as keyof typeof obj];\n delete obj[key as keyof typeof obj];\n }\n\n obj[k] = prependMongoFilterWithProjectionPrefix(obj[k], prefix);\n }\n\n return obj as unknown as Result;\n}\n\nfunction addProjectionPrefixToMongoKey(key: string, prefix: string): string {\n // MongoDB operators\n if (key[0] === '$') {\n return key;\n }\n\n return `${prefix}${key.length > 0 ? '.' : ''}${key}`;\n}\n\nexport function getMongoDBEventStore(\n options: MongoDBEventStoreOptions & { client: MongoClient },\n): MongoDBEventStore;\n\nexport function getMongoDBEventStore(\n options: MongoDBEventStoreOptions & { connectionString: string },\n): MongoDBEventStore & Closeable;\n\n// Implementation signature covers both, using a union for `options`\nexport function getMongoDBEventStore(\n options: MongoDBEventStoreOptions,\n): MongoDBEventStore | Closeable {\n const impl = new MongoDBEventStoreImplementation(options);\n\n // If a client is provided externally, we don't want to allow closing it\n if ('client' in options && 'close' in impl) {\n delete (impl as Partial<MongoDBEventStoreImplementation>).close;\n }\n\n return impl;\n}\n\n/**\n * Accepts a `streamType` (the type/category of the event stream) and an `streamId`\n * (the individual entity/object or aggregate ID) and combines them to a singular\n * `streamName` which can be used in `EventStore`.\n */\nexport function toStreamName<T extends StreamType>(\n streamType: T,\n streamId: string,\n): StreamName<T> {\n return `${streamType}:${streamId}`;\n}\n\n/**\n * Accepts a fully formatted `streamName` and returns the broken down\n * `streamType` and `streamId`.\n */\nexport function fromStreamName<T extends StreamType>(\n streamName: StreamName<T>,\n): StreamNameParts<T> {\n const parts = streamName.split(':') as [T, string];\n return {\n streamType: parts[0],\n streamId: parts[1],\n };\n}\n\n/**\n * Accepts a `streamType` (the type/category of the event stream)\n * and combines them to a `collectionName` which can be used in `EventStore`.\n */\nexport function toStreamCollectionName<T extends StreamType>(\n streamType: T,\n): StreamCollectionName<T> {\n return `emt:${streamType}`;\n}\n\n/**\n * Accepts a fully formatted `streamCollectionName` and returns the parsed `streamType`.\n */\nexport function fromStreamCollectionName<T extends StreamType>(\n streamCollectionName: StreamCollectionName<T>,\n): StreamCollectionNameParts<T> {\n const parts = streamCollectionName.split(':') as [string, T];\n return {\n streamType: parts[1],\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["uuid"],"sources":["../src/eventStore/projections/mongoDBInlineProjection.ts","../src/eventStore/projections/mongoDBInlineProjectionSpec.ts","../src/eventStore/storage/mongoDBEventStoreStorage.ts","../src/eventStore/mongoDBEventStore.ts"],"sourcesContent":["import type {\n CanHandle,\n Event,\n ProjectionDefinition,\n ProjectionHandler,\n ReadEvent,\n} from '@event-driven-io/emmett';\nimport type { Collection, Document, UpdateFilter } from 'mongodb';\nimport type {\n EventStream,\n MongoDBReadEventMetadata,\n MongoDBReadModel,\n MongoDBReadModelMetadata,\n} from '../mongoDBEventStore';\n\nexport const MongoDBDefaultInlineProjectionName = '_default';\n\nexport type MongoDBProjectionInlineHandlerContext<\n EventType extends Event = Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n> = {\n document: MongoDBReadModel | null;\n streamId: string;\n updates: UpdateFilter<EventStream<EventType, EventMetaDataType>>;\n collection: Collection<EventStream<EventType, EventMetaDataType>>;\n};\n\nexport type MongoDBInlineProjectionHandler<\n EventType extends Event = Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n> = ProjectionHandler<\n EventType,\n EventMetaDataType,\n MongoDBProjectionInlineHandlerContext\n>;\n\nexport type MongoDBInlineProjectionDefinition<\n EventType extends Event = Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n> = ProjectionDefinition<\n EventType,\n EventMetaDataType,\n MongoDBProjectionInlineHandlerContext\n> & { name: string };\n\nexport type InlineProjectionHandlerOptions<\n EventType extends Event = Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n> = {\n readModels: Record<string, MongoDBReadModel>;\n events: Array<ReadEvent<EventType, EventMetaDataType>>;\n projections: MongoDBInlineProjectionDefinition<\n EventType,\n EventMetaDataType\n >[];\n streamId: string;\n collection: Collection<EventStream>;\n updates: UpdateFilter<EventStream<Event>>;\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n client: {\n //todo: add client here\n };\n};\n\nexport const handleInlineProjections = async <\n EventType extends Event = Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n>(\n options: InlineProjectionHandlerOptions<EventType, EventMetaDataType>,\n): Promise<void> => {\n const {\n events,\n projections: allProjections,\n updates: update,\n streamId,\n collection,\n readModels,\n } = options;\n\n const eventTypes = events.map((e) => e.type);\n\n const projections = allProjections.filter((p) =>\n p.canHandle.some((type) => eventTypes.includes(type)),\n );\n\n for (const projection of projections) {\n await projection.handle(events, {\n document: readModels[projection.name] ?? null,\n streamId,\n collection,\n updates: update,\n });\n }\n};\n\nexport type MongoDBWithNotNullDocumentEvolve<\n Doc extends Document,\n EventType extends Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n> =\n | ((\n document: Doc,\n event: ReadEvent<EventType, EventMetaDataType>,\n ) => Doc | null)\n | ((document: Doc, event: ReadEvent<EventType>) => Promise<Doc | null>);\n\nexport type MongoDBWithNullableDocumentEvolve<\n Doc extends Document,\n EventType extends Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n> =\n | ((\n document: Doc | null,\n event: ReadEvent<EventType, EventMetaDataType>,\n ) => Doc | null)\n | ((\n document: Doc | null,\n event: ReadEvent<EventType>,\n ) => Promise<Doc | null>);\n\nexport type MongoDBInlineProjectionOptions<\n Doc extends Document,\n EventType extends Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n> = {\n name?: string;\n schemaVersion?: number;\n canHandle: CanHandle<EventType>;\n} & (\n | {\n evolve: MongoDBWithNullableDocumentEvolve<\n Doc,\n EventType,\n EventMetaDataType\n >;\n }\n | {\n evolve: MongoDBWithNotNullDocumentEvolve<\n Doc,\n EventType,\n EventMetaDataType\n >;\n initialState: () => Doc;\n }\n);\n\nexport const mongoDBInlineProjection = <\n Doc extends Document,\n EventType extends Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n>(\n options: MongoDBInlineProjectionOptions<Doc, EventType, EventMetaDataType>,\n): MongoDBInlineProjectionDefinition => {\n const projectionName = options.name ?? MongoDBDefaultInlineProjectionName;\n const schemaVersion = options.schemaVersion ?? 1;\n\n return {\n name: projectionName,\n canHandle: options.canHandle,\n handle: async (events, { document, updates, streamId }) => {\n if (events.length === 0) return;\n\n let state =\n 'initialState' in options\n ? (document ?? options.initialState())\n : document;\n\n for (const event of events) {\n state = await options.evolve(\n state as Doc,\n event as ReadEvent<EventType, EventMetaDataType>,\n );\n }\n\n const metadata: MongoDBReadModelMetadata = {\n streamId,\n name: projectionName,\n schemaVersion,\n streamPosition: events[events.length - 1]!.metadata.streamPosition,\n };\n\n updates.$set![`projections.${projectionName}`] =\n state !== null\n ? {\n ...state,\n _metadata: metadata,\n }\n : null;\n },\n };\n};\n","import {\n assertFails,\n AssertionError,\n assertTrue,\n deepEquals,\n isErrorConstructor,\n isSubset,\n projections,\n type Event,\n type ThenThrows,\n} from '@event-driven-io/emmett';\nimport { MongoClient, type Document } from 'mongodb';\nimport {\n getMongoDBEventStore,\n type MongoDBEventStore,\n type MongoDBEventStoreConnectionOptions,\n type MongoDBReadModel,\n type StreamName,\n} from '../mongoDBEventStore';\nimport {\n MongoDBDefaultInlineProjectionName,\n type MongoDBInlineProjectionDefinition,\n} from './mongoDBInlineProjection';\n\nexport type MongoDBInlineProjectionSpecGivenEvents<\n StreamNameType extends StreamName,\n EventType extends Event,\n> = {\n streamName: StreamNameType;\n events: EventType[];\n};\n\nexport type MongoDBInlineProjectionSpec<\n StreamNameType extends StreamName,\n EventType extends Event,\n> = (\n givenStream: MongoDBInlineProjectionSpecGivenEvents<\n StreamNameType,\n EventType\n >,\n) => {\n when: (events: EventType[]) => {\n then: (\n assert: MongoDBInlineProjectionAssert,\n message?: string,\n ) => Promise<void>;\n thenThrows: <ErrorType extends Error = Error>(\n ...args: Parameters<ThenThrows<ErrorType>>\n ) => Promise<void>;\n };\n};\n\nexport type MongoDBInlineProjectionAssertOptions<\n StreamNameType extends StreamName = StreamName,\n> = {\n streamName: StreamNameType;\n eventStore: MongoDBEventStore;\n};\n\nexport type MongoDBInlineProjectionAssert<\n StreamNameType extends StreamName = StreamName,\n> = (\n options: MongoDBInlineProjectionAssertOptions<StreamNameType>,\n) => Promise<void | boolean>;\n\nexport type MongoDBInlineProjectionSpecOptions = {\n projection: MongoDBInlineProjectionDefinition;\n} & MongoDBEventStoreConnectionOptions;\n\nexport const MongoDBInlineProjectionSpec = {\n for: <StreamNameType extends StreamName, EventType extends Event>(\n options: MongoDBInlineProjectionSpecOptions,\n ): MongoDBInlineProjectionSpec<StreamNameType, EventType> => {\n {\n const { projection, ...connectionOptions } = options;\n\n return (\n givenStream: MongoDBInlineProjectionSpecGivenEvents<\n StreamNameType,\n EventType\n >,\n ) => {\n const { streamName, events: givenEvents } = givenStream;\n return {\n when: (events: EventType[]) => {\n const allEvents = [...givenEvents, ...events];\n\n const run = (eventStore: MongoDBEventStore) =>\n eventStore.appendToStream(streamName, allEvents);\n\n return {\n then: async (\n assert: MongoDBInlineProjectionAssert,\n message?: string,\n ): Promise<void> => {\n const client =\n 'client' in connectionOptions && connectionOptions.client\n ? connectionOptions.client\n : new MongoClient(\n connectionOptions.connectionString,\n connectionOptions.clientOptions,\n );\n\n const eventStore = getMongoDBEventStore({\n projections: projections.inline([projection]),\n client,\n });\n\n try {\n await run(eventStore);\n\n const succeeded = await assert({ eventStore, streamName });\n\n if (succeeded !== undefined && succeeded === false)\n assertFails(\n message ??\n \"Projection specification didn't match the criteria\",\n );\n } finally {\n await client.close();\n }\n },\n thenThrows: async <ErrorType extends Error>(\n ...args: Parameters<ThenThrows<ErrorType>>\n ): Promise<void> => {\n const client =\n 'client' in connectionOptions && connectionOptions.client\n ? connectionOptions.client\n : new MongoClient(\n connectionOptions.connectionString,\n connectionOptions.clientOptions,\n );\n\n const eventStore = getMongoDBEventStore({\n projections: projections.inline([projection]),\n client,\n });\n\n try {\n await run(eventStore);\n throw new AssertionError('Handler did not fail as expected');\n } catch (error) {\n if (error instanceof AssertionError) throw error;\n\n if (args.length === 0) return;\n\n if (!isErrorConstructor(args[0])) {\n assertTrue(\n args[0](error as ErrorType),\n `Error didn't match the error condition: ${error?.toString()}`,\n );\n return;\n }\n\n assertTrue(\n error instanceof args[0],\n `Caught error is not an instance of the expected type: ${error?.toString()}`,\n );\n\n if (args[1]) {\n assertTrue(\n args[1](error as ErrorType),\n `Error didn't match the error condition: ${error?.toString()}`,\n );\n }\n } finally {\n await client.close();\n }\n },\n };\n },\n };\n };\n }\n },\n};\n\nexport const eventInStream = <\n StreamNameType extends StreamName,\n EventType extends Event,\n>(\n streamName: StreamNameType,\n event: EventType,\n): MongoDBInlineProjectionSpecGivenEvents<StreamNameType, EventType> => ({\n streamName,\n events: [event],\n});\n\nexport const eventsInStream = <\n StreamNameType extends StreamName,\n EventType extends Event,\n>(\n streamName: StreamNameType,\n events: EventType[],\n): MongoDBInlineProjectionSpecGivenEvents<StreamNameType, EventType> => ({\n streamName,\n events,\n});\n\nconst expectReadModelToMatch = async <\n Doc extends Document = Document,\n StreamNameType extends StreamName = StreamName,\n>(\n options: MongoDBInlineProjectionAssertOptions<StreamNameType> & {\n projectionName: string;\n match: (readModel: MongoDBReadModel<Doc> | null) => boolean;\n },\n) => {\n const { streamName, projectionName, eventStore, match } = options;\n const readModel = await eventStore.projections.inline.findOne<Doc>({\n streamName,\n projectionName,\n });\n\n return match(readModel);\n};\n\nconst expectInlineReadModelWithName = (projectionName: string) => ({\n toHave:\n <Doc extends Document, StreamNameType extends StreamName = StreamName>(\n expected: Partial<MongoDBReadModel<Doc>> | null,\n ): MongoDBInlineProjectionAssert<StreamNameType> =>\n ({ eventStore, streamName }) =>\n expectReadModelToMatch<Doc>({\n eventStore,\n streamName,\n projectionName,\n match: (readModel) => isSubset(readModel, expected),\n }),\n toDeepEquals:\n <Doc extends Document, StreamNameType extends StreamName = StreamName>(\n expected: MongoDBReadModel<Doc> | null,\n ): MongoDBInlineProjectionAssert<StreamNameType> =>\n ({ eventStore, streamName }) =>\n expectReadModelToMatch<Doc>({\n eventStore,\n streamName,\n projectionName,\n match: (readModel) => deepEquals(readModel, expected),\n }),\n toMatch:\n <Doc extends Document, StreamNameType extends StreamName = StreamName>(\n match: (readModel: MongoDBReadModel<Doc> | null) => boolean,\n ): MongoDBInlineProjectionAssert<StreamNameType> =>\n ({ eventStore, streamName }) =>\n expectReadModelToMatch<Doc>({\n eventStore,\n streamName,\n projectionName,\n match,\n }),\n notToExist:\n <\n StreamNameType extends StreamName = StreamName,\n >(): MongoDBInlineProjectionAssert<StreamNameType> =>\n ({ eventStore, streamName }) =>\n expectReadModelToMatch({\n eventStore,\n streamName,\n projectionName,\n match: (readModel) => readModel === null,\n }),\n toExist:\n (): MongoDBInlineProjectionAssert =>\n ({ eventStore, streamName }) =>\n expectReadModelToMatch({\n eventStore,\n streamName,\n projectionName,\n match: (readModel) => readModel !== null,\n }),\n});\n\nexport const expectInlineReadModel = {\n withName: (name: string) => expectInlineReadModelWithName(name),\n ...expectInlineReadModelWithName(MongoDBDefaultInlineProjectionName),\n};\n","import type { Event } from '@event-driven-io/emmett';\nimport type { Collection, Db, MongoClient } from 'mongodb';\nimport {\n toStreamCollectionName,\n type EventStream,\n type StreamType,\n} from '../mongoDBEventStore';\n\nexport type MongoDBEventStoreCollectionPerStreamTypeStorageOptions = {\n /**\n * The recommended setting where each stream type will be kept\n * in a separate collection type using the format: `emt_${streamType}`.\n */\n type: 'COLLECTION_PER_STREAM_TYPE';\n databaseName?: string;\n};\n\nexport type MongoDBEventStoreSingleCollectionStorageOptions = {\n /**\n * All streams will be kept withing a single MongDB collection\n * It'll either use default collection name (\"emt_streams\")\n * or provided name through 'collection' param.\n */\n type: 'SINGLE_COLLECTION';\n collectionName?: string;\n databaseName?: string;\n};\n\nexport type MongoDBEventStoreCollectionResolution = {\n databaseName?: string;\n collectionName: string;\n};\n\nexport type MongoDBEventStoreCustomStorageOptions = {\n /**\n * This is advanced option, where you specify your own collection\n * resolution function. You can do that by specifying the `collectionFor` function.\n */\n type: 'CUSTOM';\n databaseName?: string;\n collectionFor: <T extends StreamType>(\n streamType: T,\n ) => string | MongoDBEventStoreCollectionResolution;\n};\n\nexport type MongoDBEventStoreStorageOptions =\n | 'COLLECTION_PER_STREAM_TYPE'\n | 'SINGLE_COLLECTION'\n | MongoDBEventStoreSingleCollectionStorageOptions\n | MongoDBEventStoreCollectionPerStreamTypeStorageOptions\n | MongoDBEventStoreCustomStorageOptions;\n\nexport const DefaultMongoDBEventStoreStorageOptions =\n 'COLLECTION_PER_STREAM_TYPE';\n\nexport type MongoDBEventStoreStorage = {\n collectionFor: <T extends StreamType, EventType extends Event = Event>(\n streamType: T,\n ) => Promise<Collection<EventStream<EventType>>>;\n};\n\nexport const DefaultMongoDBEventStoreCollectionName = 'emt:streams';\n\nconst resolveCollectionAndDatabase = <T extends StreamType>(\n streamType: T,\n options: MongoDBEventStoreStorageOptions,\n): MongoDBEventStoreCollectionResolution => {\n if (\n options === 'SINGLE_COLLECTION' ||\n (typeof options === 'object' && options.type === 'SINGLE_COLLECTION')\n ) {\n return {\n collectionName:\n typeof options === 'object'\n ? (options.collectionName ?? DefaultMongoDBEventStoreCollectionName)\n : DefaultMongoDBEventStoreCollectionName,\n databaseName:\n typeof options === 'object' ? options.databaseName : undefined,\n };\n } else if (\n options === 'COLLECTION_PER_STREAM_TYPE' ||\n (typeof options === 'object' &&\n options.type === 'COLLECTION_PER_STREAM_TYPE')\n ) {\n return {\n collectionName: toStreamCollectionName(streamType),\n databaseName:\n typeof options === 'object' ? options.databaseName : undefined,\n };\n } else {\n const result = options.collectionFor(streamType);\n return {\n collectionName:\n typeof result === 'object' ? result.collectionName : result,\n databaseName:\n typeof result === 'object'\n ? (result.databaseName ?? options.databaseName)\n : options.databaseName,\n };\n }\n};\n\nconst getDB = async (options: {\n databaseName: string | undefined;\n dbsCache: Map<string, Db>;\n getConnectedClient: () => Promise<MongoClient>;\n}): Promise<Db> => {\n const { dbsCache, databaseName, getConnectedClient } = options;\n const safeDbName = databaseName ?? '___default';\n\n let db = dbsCache.get(safeDbName);\n\n if (!db) {\n const connectedClient = await getConnectedClient();\n\n db = connectedClient.db(databaseName);\n\n dbsCache.set(safeDbName, db);\n }\n\n return db;\n};\n\nconst collectionFor = async <EventType extends Event = Event>(options: {\n collectionName: string;\n streamCollections: Map<string, Collection<EventStream>>;\n db: Db;\n}): Promise<Collection<EventStream<EventType>>> => {\n const { collectionName, db, streamCollections } = options;\n\n let collection = streamCollections.get(collectionName) as\n | Collection<EventStream<EventType>>\n | undefined;\n\n if (!collection) {\n collection = db.collection<EventStream<EventType>>(collectionName);\n await collection.createIndex({ streamName: 1 }, { unique: true });\n\n streamCollections.set(\n collectionName,\n collection as Collection<EventStream>,\n );\n }\n\n return collection;\n};\n\nexport const mongoDBEventStoreStorage = (options: {\n storage?: MongoDBEventStoreStorageOptions | undefined;\n getConnectedClient: () => Promise<MongoClient>;\n}): MongoDBEventStoreStorage => {\n const dbsCache: Map<string, Db> = new Map();\n const streamCollections: Map<string, Collection<EventStream>> = new Map();\n const storageOptions =\n options.storage ?? DefaultMongoDBEventStoreStorageOptions;\n\n const { getConnectedClient } = options;\n\n return {\n collectionFor: async <\n T extends StreamType,\n EventType extends Event = Event,\n >(\n streamType: T,\n ): Promise<Collection<EventStream<EventType>>> => {\n const { collectionName, databaseName } = resolveCollectionAndDatabase(\n streamType,\n storageOptions,\n );\n\n let collection = streamCollections.get(collectionName) as\n | Collection<EventStream<EventType>>\n | undefined;\n\n if (!collection) {\n const db = await getDB({ databaseName, dbsCache, getConnectedClient });\n collection = await collectionFor<EventType>({\n collectionName,\n streamCollections,\n db,\n });\n }\n\n return collection;\n },\n };\n};\n","import {\n assertExpectedVersionMatchesCurrent,\n downcastRecordedMessage,\n ExpectedVersionConflictError,\n filterProjections,\n tryPublishMessagesAfterCommit,\n upcastRecordedMessages,\n type AggregateStreamOptions,\n type AggregateStreamResult,\n type AppendToStreamOptions,\n type AppendToStreamResult,\n type Closeable,\n type DefaultEventStoreOptions,\n type Event,\n type EventStore,\n type ProjectionRegistration,\n type ReadEvent,\n type ReadEventMetadataWithoutGlobalPosition,\n type ReadStreamOptions,\n type ReadStreamResult,\n type StreamExistsResult,\n} from '@event-driven-io/emmett';\nimport {\n MongoClient,\n type Collection,\n type Document,\n type Filter,\n type MongoClientOptions,\n type Sort,\n type UpdateFilter,\n type WithId,\n} from 'mongodb';\nimport { v4 as uuid } from 'uuid';\nimport {\n handleInlineProjections,\n MongoDBDefaultInlineProjectionName,\n type MongoDBInlineProjectionDefinition,\n type MongoDBProjectionInlineHandlerContext,\n} from './projections';\nimport {\n mongoDBEventStoreStorage,\n type MongoDBEventStoreStorage,\n type MongoDBEventStoreStorageOptions,\n} from './storage';\n\nexport const MongoDBEventStoreDefaultStreamVersion = 0n;\n\nexport type StreamType = string;\nexport type StreamName<T extends StreamType = StreamType> = `${T}:${string}`;\n\nexport type StreamNameParts<T extends StreamType = StreamType> = {\n streamType: T;\n streamId: string;\n};\n\nexport type StreamCollectionName<T extends StreamType = StreamType> =\n `emt:${T}`;\n\nexport type StreamCollectionNameParts<T extends StreamType = StreamType> = {\n streamType: T;\n};\n\nexport type MongoDBReadModelMetadata = {\n streamId: string;\n name: string;\n schemaVersion: number;\n streamPosition: bigint;\n};\n\nexport type MongoDBReadModel<Doc extends Document = Document> = Doc & {\n _metadata: MongoDBReadModelMetadata;\n};\n\nexport interface EventStream<\n EventType extends Event = Event,\n EventMetaDataType extends MongoDBReadEventMetadata = MongoDBReadEventMetadata,\n> {\n streamName: string;\n messages: Array<ReadEvent<EventType, EventMetaDataType>>;\n metadata: {\n streamId: string;\n streamType: StreamType;\n streamPosition: bigint;\n createdAt: Date;\n updatedAt: Date;\n };\n projections: Record<string, MongoDBReadModel>;\n}\n\nexport type MongoDBReadEventMetadata = ReadEventMetadataWithoutGlobalPosition;\n\nexport type MongoDBReadEvent<EventType extends Event = Event> = ReadEvent<\n EventType,\n MongoDBReadEventMetadata\n>;\n\ntype SingleProjectionQueryStreamFilter<T extends StreamType> = {\n projectionName?: string;\n} & ({ streamName: StreamName<T> } | { streamType: T; streamId?: string });\n\ntype MultiProjectionQueryStreamFilter<T extends StreamType> = {\n projectionName?: string;\n} & (\n | { streamNames: StreamName<T>[] }\n | { streamType: T; streamIds?: string[] }\n);\n\ntype MultiProjectionQueryOptions = {\n skip?: number;\n limit?: number;\n sort?: [string, 1 | -1][] | Record<string, 1 | -1>;\n};\n\n/**\n * Helpers for querying inline projections on event streams.\n */\ntype InlineProjectionQueries<T extends StreamType> = {\n /**\n * Helper for querying for a single projection. Similar to `collection.findOne`.\n * @param streamFilter - A filter object for stream level fields. If `streamType` is required if `streamName` is not provided. If `projectionName` is not provided, the default projection will be used (`MongoDBDefaultInlineProjectionName`).\n * @param projectionQuery - A MongoDB filter query based on the projection schema. Internally, this function will prepend each object key with the necessary projection name.\n */\n findOne: <Doc extends Document>(\n streamFilter: SingleProjectionQueryStreamFilter<T>,\n projectionQuery?: Filter<MongoDBReadModel<Doc>>,\n ) => Promise<MongoDBReadModel<Doc> | null>;\n /**\n * Helper for querying for multiple projections. Similar to `collection.find`.\n *\n * ***NOTE***: If `streamFilter.streamNames` is an empty array, this function will return an empty array. If `streamFilter.streamIds` is an empty array, the `streamIds` filter will not be used.\n *\n * @param streamFilter - A filter object for stream level fields. If `streamType` is required if `streamNames` is not provided. If `projectionName` is not provided, the default projection will be used (`MongoDBDefaultInlineProjectionName`).\n * @param projectionQuery - A MongoDB filter query based on the projection schema. Internally, this function will prepend each object key with the necessary projection name.\n * @param queryOptions - Additional query options like `skip`, `limit`, and `sort`. `sort`, similar to `projectionQuery`, will prepend each object key with the necessary projection name.\n */\n find: <Doc extends Document>(\n streamFilter: MultiProjectionQueryStreamFilter<T>,\n projectionQuery?: Filter<MongoDBReadModel<Doc>>,\n queryOptions?: MultiProjectionQueryOptions,\n ) => Promise<MongoDBReadModel<Doc>[]>;\n /**\n * Returns the total number of documents matching the provided filter options. Similar to `collection.countDocuments`.\n *\n * ***NOTE***: If `streamFilter.streamNames` is an empty array, this function will return `0`. If `streamFilter.streamIds` is an empty array, the `streamIds` filter will not be used.\n *\n * @param streamFilter - A filter object for stream level fields. If `streamType` is required if `streamNames` is not provided. If `projectionName` is not provided, the default projection will be used (`MongoDBDefaultInlineProjectionName`).\n * @param projectionQuery - A MongoDB filter query based on the projection schema. Internally, this function will prepend each object key with the necessary projection name.\n */\n count: <Doc extends Document>(\n streamFilter: MultiProjectionQueryStreamFilter<T>,\n projectionQuery?: Filter<MongoDBReadModel<Doc>>,\n ) => Promise<number>;\n};\n\n/**\n * Helpers for querying projections on event streams.\n */\ntype ProjectionQueries<T extends StreamType> = {\n inline: InlineProjectionQueries<T>;\n};\n\nexport type MongoDBEventStoreClientOptions = {\n client: MongoClient;\n connectionString?: never;\n clientOptions?: never;\n};\n\nexport type MongoDBEventStoreConnectionStringOptions = {\n client?: never;\n connectionString: string;\n clientOptions?: MongoClientOptions;\n};\n\nexport type MongoDBEventStoreConnectionOptions =\n | MongoDBEventStoreClientOptions\n | MongoDBEventStoreConnectionStringOptions;\n\nexport type MongoDBEventStoreOptions = {\n projections?: ProjectionRegistration<\n 'inline',\n MongoDBReadEventMetadata,\n MongoDBProjectionInlineHandlerContext\n >[];\n storage?: MongoDBEventStoreStorageOptions;\n} & MongoDBEventStoreConnectionOptions &\n DefaultEventStoreOptions<MongoDBEventStore>;\n\nexport type MongoDBEventStore = EventStore<MongoDBReadEventMetadata> & {\n projections: ProjectionQueries<StreamType>;\n collectionFor: <EventType extends Event>(\n streamType: StreamType,\n ) => Promise<Collection<EventStream<EventType>>>;\n};\n\nclass MongoDBEventStoreImplementation implements MongoDBEventStore, Closeable {\n private readonly client: MongoClient;\n private readonly inlineProjections: MongoDBInlineProjectionDefinition[];\n private shouldManageClientLifetime: boolean;\n private isClosed: boolean = false;\n private storage: MongoDBEventStoreStorage;\n private options: MongoDBEventStoreOptions;\n public projections: ProjectionQueries<StreamType>;\n\n constructor(options: MongoDBEventStoreOptions) {\n this.options = options;\n this.client =\n 'client' in options && options.client\n ? options.client\n : new MongoClient(options.connectionString, options.clientOptions);\n this.shouldManageClientLifetime = !('client' in options);\n this.storage = mongoDBEventStoreStorage({\n storage: options.storage,\n getConnectedClient: () => this.getConnectedClient(),\n });\n this.inlineProjections = filterProjections(\n 'inline',\n options.projections ?? [],\n ) as MongoDBInlineProjectionDefinition[];\n\n this.projections = {\n inline: {\n findOne: this.findOneInlineProjection.bind(this),\n find: this.findInlineProjection.bind(this),\n count: this.countInlineProjection.bind(this),\n },\n };\n }\n\n async readStream<\n EventType extends Event,\n EventPayloadType extends Event = EventType,\n >(\n streamName: StreamName,\n options?: ReadStreamOptions<EventType, EventPayloadType>,\n ): Promise<\n Exclude<ReadStreamResult<EventType, MongoDBReadEventMetadata>, null>\n > {\n const { streamType } = fromStreamName(streamName);\n const expectedStreamVersion = options?.expectedStreamVersion;\n\n const collection = await this.storage.collectionFor(streamType);\n\n const filter = {\n streamName: { $eq: streamName },\n };\n\n const eventsSliceArr: number[] = [];\n\n if (options && 'from' in options) {\n eventsSliceArr.push(Number(options.from));\n } else {\n eventsSliceArr.push(0);\n }\n\n if (options && 'to' in options) {\n eventsSliceArr.push(Number(options.to));\n }\n\n const eventsSlice =\n eventsSliceArr.length > 1 ? { $slice: eventsSliceArr } : 1;\n\n const stream = await collection.findOne<\n WithId<Pick<EventStream<EventPayloadType>, 'metadata' | 'messages'>>\n >(filter, {\n useBigInt64: true,\n projection: {\n metadata: 1,\n messages: eventsSlice,\n },\n });\n\n if (!stream) {\n return {\n events: [],\n currentStreamVersion: MongoDBEventStoreDefaultStreamVersion,\n streamExists: false,\n };\n }\n\n assertExpectedVersionMatchesCurrent(\n stream.metadata.streamPosition,\n expectedStreamVersion,\n MongoDBEventStoreDefaultStreamVersion,\n );\n\n const events = upcastRecordedMessages(\n stream.messages,\n options?.schema?.versioning,\n );\n\n return {\n events,\n currentStreamVersion: stream.metadata.streamPosition,\n streamExists: true,\n };\n }\n\n async aggregateStream<\n State,\n EventType extends Event,\n EventPayloadType extends Event = EventType,\n >(\n streamName: StreamName,\n options: AggregateStreamOptions<\n State,\n EventType,\n MongoDBReadEventMetadata,\n EventPayloadType\n >,\n ): Promise<AggregateStreamResult<State>> {\n const stream = await this.readStream<EventType, EventPayloadType>(\n streamName,\n options?.read,\n );\n const { evolve, initialState } = options;\n\n const state = stream.events.reduce(evolve, initialState());\n return {\n state,\n currentStreamVersion: stream.currentStreamVersion,\n streamExists: stream.streamExists,\n };\n }\n\n async appendToStream<\n EventType extends Event,\n EventPayloadType extends Event = EventType,\n >(\n streamName: StreamName,\n events: EventType[],\n options?: AppendToStreamOptions<EventType, EventPayloadType>,\n ): Promise<AppendToStreamResult> {\n const { streamId, streamType } = fromStreamName(streamName);\n const expectedStreamVersion = options?.expectedStreamVersion;\n\n const collection = await this.storage.collectionFor(streamType);\n\n const stream = await collection.findOne<\n WithId<Pick<EventStream<EventPayloadType>, 'metadata' | 'projections'>>\n >(\n { streamName: { $eq: streamName } },\n {\n useBigInt64: true,\n projection: {\n 'metadata.streamPosition': 1,\n projections: 1,\n },\n },\n );\n\n const currentStreamVersion =\n stream?.metadata.streamPosition ?? MongoDBEventStoreDefaultStreamVersion;\n\n assertExpectedVersionMatchesCurrent(\n currentStreamVersion,\n expectedStreamVersion,\n MongoDBEventStoreDefaultStreamVersion,\n );\n\n let streamOffset = currentStreamVersion;\n\n const eventsToAppend: ReadEvent<\n EventPayloadType,\n MongoDBReadEventMetadata\n >[] = events.map((event) => {\n const metadata: MongoDBReadEventMetadata = {\n messageId: uuid(),\n streamName,\n streamPosition: ++streamOffset,\n };\n return downcastRecordedMessage(\n {\n type: event.type,\n data: event.data,\n metadata: {\n ...metadata,\n ...('metadata' in event ? (event.metadata ?? {}) : {}),\n },\n } as ReadEvent<EventType, MongoDBReadEventMetadata>,\n options?.schema?.versioning,\n );\n });\n\n const now = new Date();\n const updates: UpdateFilter<EventStream> = {\n $push: { messages: { $each: eventsToAppend } },\n $set: {\n 'metadata.updatedAt': now,\n 'metadata.streamPosition': currentStreamVersion + BigInt(events.length),\n },\n $setOnInsert: {\n streamName,\n 'metadata.streamId': streamId,\n 'metadata.streamType': streamType,\n 'metadata.createdAt': now,\n },\n };\n\n if (this.inlineProjections) {\n await handleInlineProjections({\n readModels: stream?.projections ?? {},\n streamId,\n events: eventsToAppend,\n projections: this.inlineProjections,\n collection,\n updates,\n client: {},\n });\n }\n\n const updatedStream = await collection.updateOne(\n {\n streamName: { $eq: streamName },\n 'metadata.streamPosition': currentStreamVersion,\n },\n updates,\n { useBigInt64: true, upsert: true },\n );\n\n if (!updatedStream) {\n throw new ExpectedVersionConflictError(\n currentStreamVersion,\n options?.expectedStreamVersion ?? 0n,\n );\n }\n\n await tryPublishMessagesAfterCommit<MongoDBEventStore>(\n eventsToAppend,\n this.options.hooks,\n // {\n // TODO: same context as InlineProjectionHandlerContext for mongodb?\n // },\n );\n\n return {\n nextExpectedStreamVersion:\n currentStreamVersion + BigInt(eventsToAppend.length),\n createdNewStream:\n currentStreamVersion === MongoDBEventStoreDefaultStreamVersion,\n };\n }\n\n async streamExists(streamName: StreamName): Promise<StreamExistsResult> {\n const { streamType } = fromStreamName(streamName);\n\n const collection = await this.storage.collectionFor(streamType);\n\n const filter = {\n streamName: { $eq: streamName },\n };\n\n const count = await collection.countDocuments(filter, {\n useBigInt64: true,\n limit: 1,\n });\n\n return Boolean(count > 0);\n }\n\n collectionFor = async <EventType extends Event>(\n streamType: StreamType,\n ): Promise<Collection<EventStream<EventType>>> => {\n return this.storage.collectionFor(streamType);\n };\n\n /**\n * Gracefully cleans up managed resources by the MongoDBEventStore.\n * It closes MongoDB client created for the provided connection string\n * through event store options.\n *\n * @memberof Closeable\n */\n close = (): Promise<void> => {\n if (this.isClosed) return Promise.resolve();\n\n this.isClosed = true;\n if (!this.shouldManageClientLifetime) return Promise.resolve();\n\n return this.client.close();\n };\n\n private async findOneInlineProjection<Doc extends Document>(\n streamFilter: SingleProjectionQueryStreamFilter<StreamType>,\n projectionQuery?: Filter<MongoDBReadModel<Doc>>,\n ) {\n const { projectionName, streamName, streamType } =\n parseSingleProjectionQueryStreamFilter(streamFilter);\n const collection = await this.storage.collectionFor(streamType);\n const query = prependMongoFilterWithProjectionPrefix<\n Filter<MongoDBReadModel<Doc>> | undefined,\n Filter<EventStream> | undefined\n >(projectionQuery, `projections.${projectionName}`);\n\n const filters: Filter<EventStream>[] = [\n { [`projections.${projectionName}`]: { $exists: true } },\n ];\n\n if (query) {\n filters.push(query);\n }\n\n if (streamName) {\n filters.push({ streamName: { $eq: streamName } });\n }\n\n const result = await collection.findOne<{\n projections: Record<typeof projectionName, MongoDBReadModel<Doc>>;\n }>(\n { $and: filters },\n {\n useBigInt64: true,\n projection: { [`projections.${projectionName}`]: 1 },\n },\n );\n\n return result?.projections?.[projectionName] ?? null;\n }\n\n private async findInlineProjection<Doc extends Document>(\n streamFilter: MultiProjectionQueryStreamFilter<StreamType>,\n projectionQuery?: Filter<MongoDBReadModel<Doc>>,\n queryOptions?: MultiProjectionQueryOptions,\n ) {\n const parsedStreamFilter =\n parseMultiProjectionQueryStreamFilter(streamFilter);\n if (!parsedStreamFilter) return [];\n const { projectionName, streamNames, streamType } = parsedStreamFilter;\n\n const collection = await this.storage.collectionFor(streamType);\n const prefix = `projections.${projectionName}`;\n const projectionFilter = prependMongoFilterWithProjectionPrefix<\n Filter<MongoDBReadModel<Doc>> | undefined,\n Filter<EventStream> | undefined\n >(projectionQuery, prefix);\n\n const filters: Filter<EventStream>[] = [\n { [`projections.${projectionName}`]: { $ne: null } },\n ];\n\n if (projectionFilter) {\n filters.push(projectionFilter);\n }\n\n if (streamNames) {\n filters.push({ streamName: { $in: streamNames } });\n }\n\n let query = collection.find<\n EventStream & {\n projections: Record<typeof projectionName, MongoDBReadModel<Doc>>;\n }\n >(\n { $and: filters },\n {\n useBigInt64: true,\n projection: { [`projections.${projectionName}`]: 1 },\n },\n );\n\n if (queryOptions?.skip) {\n query = query.skip(queryOptions.skip);\n }\n\n if (queryOptions?.limit) {\n query = query.limit(queryOptions.limit);\n }\n\n if (queryOptions?.sort) {\n const sort = prependMongoFilterWithProjectionPrefix<Sort>(\n queryOptions.sort,\n prefix,\n );\n query = query.sort(sort);\n }\n\n const streams = await query.toArray();\n\n return streams\n .map((s) => s.projections[projectionName])\n .filter((p): p is MongoDBReadModel<Doc> => !!p);\n }\n\n private async countInlineProjection<Doc extends Document>(\n streamFilter: MultiProjectionQueryStreamFilter<StreamType>,\n projectionQuery?: Filter<MongoDBReadModel<Doc>>,\n ) {\n const parsedStreamFilter =\n parseMultiProjectionQueryStreamFilter(streamFilter);\n if (!parsedStreamFilter) return 0;\n const { projectionName, streamNames, streamType } = parsedStreamFilter;\n\n const collection = await this.storage.collectionFor(streamType);\n const prefix = `projections.${projectionName}`;\n const projectionFilter = prependMongoFilterWithProjectionPrefix<\n Filter<MongoDBReadModel<Doc>> | undefined,\n Filter<EventStream> | undefined\n >(projectionQuery, prefix);\n\n const filters: Filter<EventStream>[] = [\n { [`projections.${projectionName}`]: { $ne: null } },\n ];\n\n if (projectionFilter) {\n filters.push(projectionFilter);\n }\n\n if (streamNames) {\n filters.push({ streamName: { $in: streamNames } });\n }\n\n const total = await collection.countDocuments({ $and: filters });\n return total;\n }\n\n private getConnectedClient = async (): Promise<MongoClient> => {\n if (!this.isClosed) await this.client.connect();\n return this.client;\n };\n}\n\nfunction parseSingleProjectionQueryStreamFilter<\n T extends StreamType = StreamType,\n>(streamFilter: SingleProjectionQueryStreamFilter<T>) {\n const projectionName =\n streamFilter.projectionName ?? MongoDBDefaultInlineProjectionName;\n\n if ('streamName' in streamFilter) {\n const { streamType } = fromStreamName(streamFilter.streamName);\n return {\n projectionName,\n streamName: streamFilter.streamName,\n streamType,\n };\n }\n\n if (streamFilter.streamId) {\n const streamName = toStreamName(\n streamFilter.streamType,\n streamFilter.streamId,\n );\n return {\n projectionName,\n streamName,\n streamType: streamFilter.streamType,\n };\n }\n\n return {\n projectionName,\n streamType: streamFilter.streamType,\n };\n}\n\nfunction parseMultiProjectionQueryStreamFilter<T extends StreamType>(\n streamFilter: MultiProjectionQueryStreamFilter<T>,\n) {\n const projectionName =\n streamFilter.projectionName ?? MongoDBDefaultInlineProjectionName;\n\n if ('streamNames' in streamFilter) {\n if (streamFilter.streamNames.length == 0) return null;\n const { streamType } = fromStreamName(streamFilter.streamNames[0]!);\n return {\n projectionName,\n streamNames: streamFilter.streamNames,\n streamType,\n };\n }\n\n if (streamFilter.streamIds && streamFilter.streamIds.length > 0) {\n const streamNames = streamFilter.streamIds.map((id) =>\n toStreamName(streamFilter.streamType, id),\n );\n return {\n projectionName,\n streamNames,\n streamType: streamFilter.streamType,\n };\n }\n\n return {\n projectionName,\n streamType: streamFilter.streamType,\n };\n}\n\n/**\n * Prepends `prefix` to all object keys that don't start with a '$'\n */\nexport function prependMongoFilterWithProjectionPrefix<T, Result = T>(\n obj: T,\n prefix: string,\n): Result {\n if (typeof obj !== 'object' || obj === null || obj === undefined) {\n return obj as unknown as Result;\n }\n\n if (Array.isArray(obj)) {\n for (let i = 0; i < obj.length; i++) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n obj[i] = prependMongoFilterWithProjectionPrefix(obj[i], prefix);\n }\n return obj as unknown as Result;\n }\n\n for (const key in obj) {\n // @ts-expect-error we're forcing `k` to be a key of `T`\n const k: keyof typeof obj = addProjectionPrefixToMongoKey(key, prefix);\n if (k !== key) {\n obj[k] = obj[key as keyof typeof obj];\n delete obj[key as keyof typeof obj];\n }\n\n obj[k] = prependMongoFilterWithProjectionPrefix(obj[k], prefix);\n }\n\n return obj as unknown as Result;\n}\n\nfunction addProjectionPrefixToMongoKey(key: string, prefix: string): string {\n // MongoDB operators\n if (key[0] === '$') {\n return key;\n }\n\n return `${prefix}${key.length > 0 ? '.' : ''}${key}`;\n}\n\nexport function getMongoDBEventStore(\n options: MongoDBEventStoreOptions & { client: MongoClient },\n): MongoDBEventStore;\n\nexport function getMongoDBEventStore(\n options: MongoDBEventStoreOptions & { connectionString: string },\n): MongoDBEventStore & Closeable;\n\n// Implementation signature covers both, using a union for `options`\nexport function getMongoDBEventStore(\n options: MongoDBEventStoreOptions,\n): MongoDBEventStore | Closeable {\n const impl = new MongoDBEventStoreImplementation(options);\n\n // If a client is provided externally, we don't want to allow closing it\n if ('client' in options && 'close' in impl) {\n delete (impl as Partial<MongoDBEventStoreImplementation>).close;\n }\n\n return impl;\n}\n\n/**\n * Accepts a `streamType` (the type/category of the event stream) and an `streamId`\n * (the individual entity/object or aggregate ID) and combines them to a singular\n * `streamName` which can be used in `EventStore`.\n */\nexport function toStreamName<T extends StreamType>(\n streamType: T,\n streamId: string,\n): StreamName<T> {\n return `${streamType}:${streamId}`;\n}\n\n/**\n * Accepts a fully formatted `streamName` and returns the broken down\n * `streamType` and `streamId`.\n */\nexport function fromStreamName<T extends StreamType>(\n streamName: StreamName<T>,\n): StreamNameParts<T> {\n const parts = streamName.split(':') as [T, string];\n return {\n streamType: parts[0],\n streamId: parts[1],\n };\n}\n\n/**\n * Accepts a `streamType` (the type/category of the event stream)\n * and combines them to a `collectionName` which can be used in `EventStore`.\n */\nexport function toStreamCollectionName<T extends StreamType>(\n streamType: T,\n): StreamCollectionName<T> {\n return `emt:${streamType}`;\n}\n\n/**\n * Accepts a fully formatted `streamCollectionName` and returns the parsed `streamType`.\n */\nexport function fromStreamCollectionName<T extends StreamType>(\n streamCollectionName: StreamCollectionName<T>,\n): StreamCollectionNameParts<T> {\n const parts = streamCollectionName.split(':') as [string, T];\n return {\n streamType: parts[1],\n };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@event-driven-io/emmett-mongodb",
3
3
  "type": "module",
4
- "version": "0.43.0-beta.14",
4
+ "version": "0.43.0-beta.15",
5
5
  "description": "Emmett - MongoDB - Event Sourcing development made simple",
6
6
  "scripts": {
7
7
  "build": "tsdown",
@@ -47,11 +47,11 @@
47
47
  "dist"
48
48
  ],
49
49
  "devDependencies": {
50
- "@event-driven-io/emmett-testcontainers": "0.43.0-beta.14",
50
+ "@event-driven-io/emmett-testcontainers": "0.43.0-beta.15",
51
51
  "@testcontainers/mongodb": "^11.14.0"
52
52
  },
53
53
  "peerDependencies": {
54
- "@event-driven-io/emmett": "0.43.0-beta.14",
54
+ "@event-driven-io/emmett": "0.43.0-beta.15",
55
55
  "mongodb": "^7.2.0"
56
56
  }
57
57
  }