@event-driven-io/emmett-postgresql 0.38.5 → 0.38.7

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,"sources":["/home/runner/work/emmett/emmett/src/packages/emmett-postgresql/dist/index.cjs","../src/eventStore/postgreSQLEventStore.ts","../../emmett/src/validation/index.ts","../../emmett/src/errors/index.ts","../../emmett/src/eventStore/inMemoryEventStore.ts","../../emmett/src/database/inMemoryDatabase.ts","../../emmett/src/utils/retry.ts","../../emmett/src/eventStore/projections/inMemory/inMemoryProjectionSpec.ts","../../emmett/src/eventStore/subscriptions/caughtUpTransformStream.ts","../../emmett/src/eventStore/subscriptions/streamingCoordinator.ts","../../emmett/src/streaming/transformations/notifyAboutNoActiveReaders.ts","../../emmett/src/streaming/generators/fromArray.ts","../../emmett/src/streaming/restream.ts","../../emmett/src/streaming/transformations/filter.ts","../../emmett/src/streaming/transformations/map.ts","../../emmett/src/streaming/transformations/reduce.ts","../../emmett/src/streaming/transformations/retry.ts","../../emmett/src/streaming/transformations/skip.ts","../../emmett/src/streaming/transformations/stopAfter.ts","../../emmett/src/streaming/transformations/stopOn.ts","../../emmett/src/streaming/transformations/take.ts","../../emmett/src/streaming/transformations/waitAtMost.ts","../../emmett/src/eventStore/expectedVersion.ts","../../emmett/src/utils/deepEquals.ts","../../emmett/src/serialization/json/JSONParser.ts","../../emmett/src/testing/assertions.ts","../../emmett/src/processors/processors.ts","../../emmett/src/projections/index.ts","../../emmett/src/streaming/transformations/index.ts","../src/eventStore/consumers/messageBatchProcessing/index.ts","../src/eventStore/schema/readLastMessageGlobalPosition.ts","../src/eventStore/schema/typing.ts","../src/eventStore/schema/readMessagesBatch.ts","../src/eventStore/consumers/postgreSQLEventStoreConsumer.ts","../src/eventStore/consumers/postgreSQLProcessor.ts","../src/eventStore/schema/index.ts","../src/eventStore/schema/appendToStream.ts","../src/eventStore/schema/storeProcessorCheckpoint.ts","../src/eventStore/schema/tables.ts","../src/eventStore/schema/readProcessorCheckpoint.ts","../src/eventStore/schema/readStream.ts","../src/eventStore/consumers/rebuildPostgreSQLProjections.ts","../src/eventStore/projections/pongo/pongoProjections.ts","../src/eventStore/projections/pongo/pongoProjectionSpec.ts","../src/eventStore/projections/postgresProjectionSpec.ts","../src/eventStore/projections/postgreSQLProjection.ts"],"names":[],"mappings":"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","file":"/home/runner/work/emmett/emmett/src/packages/emmett-postgresql/dist/index.cjs","sourcesContent":[null,"import {\n dumbo,\n type MigrationStyle,\n type NodePostgresClientConnection,\n type NodePostgresConnector,\n type NodePostgresPool,\n type NodePostgresPoolClientConnection,\n} from '@event-driven-io/dumbo';\nimport {\n assertExpectedVersionMatchesCurrent,\n ExpectedVersionConflictError,\n NO_CONCURRENCY_CHECK,\n type AggregateStreamOptions,\n type AggregateStreamResult,\n type AppendToStreamOptions,\n type AppendToStreamResultWithGlobalPosition,\n type Event,\n type EventStore,\n type EventStoreSession,\n type EventStoreSessionFactory,\n type ProjectionRegistration,\n type ReadEvent,\n type ReadEventMetadataWithGlobalPosition,\n type ReadStreamOptions,\n type ReadStreamResult,\n} from '@event-driven-io/emmett';\nimport pg from 'pg';\nimport {\n postgreSQLEventStoreConsumer,\n type PostgreSQLEventStoreConsumer,\n type PostgreSQLEventStoreConsumerConfig,\n} from './consumers';\nimport {\n handleProjections,\n type PostgreSQLProjectionHandlerContext,\n} from './projections';\nimport {\n appendToStream,\n createEventStoreSchema,\n readStream,\n schemaSQL,\n type AppendToStreamBeforeCommitHook,\n} from './schema';\n\nexport interface PostgresEventStore\n extends EventStore<PostgresReadEventMetadata>,\n EventStoreSessionFactory<PostgresEventStore> {\n appendToStream<EventType extends Event>(\n streamName: string,\n events: EventType[],\n options?: AppendToStreamOptions,\n ): Promise<AppendToStreamResultWithGlobalPosition>;\n consumer<ConsumerEventType extends Event = Event>(\n options?: PostgreSQLEventStoreConsumerConfig<ConsumerEventType>,\n ): PostgreSQLEventStoreConsumer<ConsumerEventType>;\n close(): Promise<void>;\n schema: {\n sql(): string;\n print(): void;\n migrate(): Promise<void>;\n };\n}\n\nexport type PostgresReadEventMetadata = ReadEventMetadataWithGlobalPosition;\n\nexport type PostgresReadEvent<EventType extends Event = Event> = ReadEvent<\n EventType,\n PostgresReadEventMetadata\n>;\n\ntype PostgresEventStorePooledOptions =\n | {\n connector?: NodePostgresConnector;\n connectionString?: string;\n database?: string;\n pooled: true;\n pool: pg.Pool;\n }\n | {\n connector?: NodePostgresConnector;\n connectionString?: string;\n database?: string;\n pool: pg.Pool;\n }\n | {\n connector?: NodePostgresConnector;\n connectionString?: string;\n database?: string;\n pooled: true;\n }\n | {\n connector?: NodePostgresConnector;\n connectionString?: string;\n database?: string;\n };\n\ntype PostgresEventStoreNotPooledOptions =\n | {\n connector?: NodePostgresConnector;\n connectionString?: string;\n database?: string;\n pooled: false;\n client: pg.Client;\n }\n | {\n connector?: NodePostgresConnector;\n connectionString?: string;\n database?: string;\n client: pg.Client;\n }\n | {\n connector?: NodePostgresConnector;\n connectionString?: string;\n database?: string;\n pooled: false;\n }\n | {\n connector?: NodePostgresConnector;\n connectionString?: string;\n database?: string;\n connection:\n | NodePostgresPoolClientConnection\n | NodePostgresClientConnection;\n pooled?: false;\n }\n | {\n connector?: NodePostgresConnector;\n connectionString?: string;\n database?: string;\n dumbo: NodePostgresPool;\n pooled?: false;\n };\n\nexport type PostgresEventStoreConnectionOptions =\n | PostgresEventStorePooledOptions\n | PostgresEventStoreNotPooledOptions;\n\nexport type PostgresEventStoreOptions = {\n projections?: ProjectionRegistration<\n 'inline',\n PostgresReadEventMetadata,\n PostgreSQLProjectionHandlerContext\n >[];\n schema?: { autoMigration?: MigrationStyle };\n connectionOptions?: PostgresEventStoreConnectionOptions;\n hooks?: {\n /**\n * This hook will be called **AFTER** event store schema was created\n */\n onAfterSchemaCreated?: () => Promise<void> | void;\n };\n};\n\nexport const defaultPostgreSQLOptions: PostgresEventStoreOptions = {\n projections: [],\n schema: { autoMigration: 'CreateOrUpdate' },\n};\n\nexport const PostgreSQLEventStoreDefaultStreamVersion = 0n;\n\nexport const getPostgreSQLEventStore = (\n connectionString: string,\n options: PostgresEventStoreOptions = defaultPostgreSQLOptions,\n): PostgresEventStore => {\n const poolOptions = {\n connectionString,\n ...(options.connectionOptions ? options.connectionOptions : {}),\n };\n const pool = 'dumbo' in poolOptions ? poolOptions.dumbo : dumbo(poolOptions);\n let migrateSchema: Promise<void> | undefined = undefined;\n\n const autoGenerateSchema =\n options.schema?.autoMigration === undefined ||\n options.schema?.autoMigration !== 'None';\n\n const ensureSchemaExists = () => {\n if (!autoGenerateSchema) return Promise.resolve();\n\n if (!migrateSchema) {\n migrateSchema = createEventStoreSchema(pool).then(async () => {\n if (options.hooks?.onAfterSchemaCreated) {\n await options.hooks.onAfterSchemaCreated();\n }\n });\n }\n return migrateSchema;\n };\n\n const inlineProjections = (options.projections ?? [])\n .filter(({ type }) => type === 'inline')\n .map(({ projection }) => projection);\n\n const beforeCommitHook: AppendToStreamBeforeCommitHook | undefined =\n inlineProjections.length > 0\n ? (events, { transaction }) =>\n handleProjections({\n projections: inlineProjections,\n connection: {\n connectionString,\n pool,\n transaction,\n },\n // TODO: Add proper handling of global data\n // Currently it's not available as append doesn't return array of global position but just the last one\n events: events as ReadEvent<Event, PostgresReadEventMetadata>[],\n })\n : undefined;\n\n return {\n schema: {\n sql: () => schemaSQL.join(''),\n print: () => console.log(schemaSQL.join('')),\n migrate: async () => {\n await (migrateSchema = createEventStoreSchema(pool));\n },\n },\n async aggregateStream<State, EventType extends Event>(\n streamName: string,\n options: AggregateStreamOptions<\n State,\n EventType,\n PostgresReadEventMetadata\n >,\n ): Promise<AggregateStreamResult<State>> {\n const { evolve, initialState, read } = options;\n\n const expectedStreamVersion = read?.expectedStreamVersion;\n\n let state = initialState();\n\n const result = await this.readStream<EventType>(streamName, options.read);\n const currentStreamVersion = result.currentStreamVersion;\n\n assertExpectedVersionMatchesCurrent(\n currentStreamVersion,\n expectedStreamVersion,\n PostgreSQLEventStoreDefaultStreamVersion,\n );\n\n for (const event of result.events) {\n if (!event) continue;\n\n state = evolve(state, event);\n }\n\n return {\n currentStreamVersion: currentStreamVersion,\n state,\n streamExists: result.streamExists,\n };\n },\n\n readStream: async <EventType extends Event>(\n streamName: string,\n options?: ReadStreamOptions,\n ): Promise<ReadStreamResult<EventType, PostgresReadEventMetadata>> => {\n await ensureSchemaExists();\n return readStream<EventType>(pool.execute, streamName, options);\n },\n\n appendToStream: async <EventType extends Event>(\n streamName: string,\n events: EventType[],\n options?: AppendToStreamOptions,\n ): Promise<AppendToStreamResultWithGlobalPosition> => {\n await ensureSchemaExists();\n // TODO: This has to be smarter when we introduce urn-based resolution\n const [firstPart, ...rest] = streamName.split('-');\n\n const streamType =\n firstPart && rest.length > 0 ? firstPart : 'emt:unknown';\n\n const appendResult = await appendToStream(\n pool,\n streamName,\n streamType,\n events,\n {\n ...options,\n beforeCommitHook,\n },\n );\n\n if (!appendResult.success)\n throw new ExpectedVersionConflictError<bigint>(\n -1n, //TODO: Return actual version in case of error\n options?.expectedStreamVersion ?? NO_CONCURRENCY_CHECK,\n );\n\n return {\n nextExpectedStreamVersion: appendResult.nextStreamPosition,\n lastEventGlobalPosition:\n appendResult.globalPositions[\n appendResult.globalPositions.length - 1\n ]!,\n createdNewStream:\n appendResult.nextStreamPosition >= BigInt(events.length),\n };\n },\n consumer: <ConsumerEventType extends Event = Event>(\n options?: PostgreSQLEventStoreConsumerConfig<ConsumerEventType>,\n ): PostgreSQLEventStoreConsumer<ConsumerEventType> =>\n postgreSQLEventStoreConsumer<ConsumerEventType>({\n ...(options ?? {}),\n pool,\n connectionString,\n }),\n close: () => pool.close(),\n\n async withSession<T = unknown>(\n callback: (session: EventStoreSession<PostgresEventStore>) => Promise<T>,\n ): Promise<T> {\n return await pool.withConnection(async (connection) => {\n const storeOptions: PostgresEventStoreOptions = {\n ...options,\n connectionOptions: {\n connection,\n },\n schema: {\n ...(options.schema ?? {}),\n autoMigration: 'None',\n },\n };\n\n const eventStore = getPostgreSQLEventStore(\n connectionString,\n storeOptions,\n );\n\n return ensureSchemaExists().then(() =>\n callback({\n eventStore,\n close: () => Promise.resolve(),\n }),\n );\n });\n },\n };\n};\n","import { ValidationError } from '../errors';\n\nexport const enum ValidationErrors {\n NOT_A_NONEMPTY_STRING = 'NOT_A_NONEMPTY_STRING',\n NOT_A_POSITIVE_NUMBER = 'NOT_A_POSITIVE_NUMBER',\n NOT_AN_UNSIGNED_BIGINT = 'NOT_AN_UNSIGNED_BIGINT',\n}\n\nexport const isNumber = (val: unknown): val is number =>\n typeof val === 'number' && val === val;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport const isBigint = (val: any): val is bigint =>\n typeof val === 'bigint' && val === val;\n\nexport const isString = (val: unknown): val is string =>\n typeof val === 'string';\n\nexport const assertNotEmptyString = (value: unknown): string => {\n if (!isString(value) || value.length === 0) {\n throw new ValidationError(ValidationErrors.NOT_A_NONEMPTY_STRING);\n }\n return value;\n};\n\nexport const assertPositiveNumber = (value: unknown): number => {\n if (!isNumber(value) || value <= 0) {\n throw new ValidationError(ValidationErrors.NOT_A_POSITIVE_NUMBER);\n }\n return value;\n};\n\nexport const assertUnsignedBigInt = (value: string): bigint => {\n const number = BigInt(value);\n if (number < 0) {\n throw new ValidationError(ValidationErrors.NOT_AN_UNSIGNED_BIGINT);\n }\n return number;\n};\n\nexport * from './dates';\n","import { isNumber, isString } from '../validation';\n\nexport type ErrorConstructor<ErrorType extends Error> = new (\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ...args: any[]\n) => ErrorType;\n\nexport const isErrorConstructor = <ErrorType extends Error>(\n // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n expect: Function,\n): expect is ErrorConstructor<ErrorType> => {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return (\n typeof expect === 'function' &&\n expect.prototype &&\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n expect.prototype.constructor === expect\n );\n};\n\nexport class EmmettError extends Error {\n public errorCode: number;\n\n constructor(\n options?: { errorCode: number; message?: string } | string | number,\n ) {\n const errorCode =\n options && typeof options === 'object' && 'errorCode' in options\n ? options.errorCode\n : isNumber(options)\n ? options\n : 500;\n const message =\n options && typeof options === 'object' && 'message' in options\n ? options.message\n : isString(options)\n ? options\n : `Error with status code '${errorCode}' ocurred during Emmett processing`;\n\n super(message);\n this.errorCode = errorCode;\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, EmmettError.prototype);\n }\n\n public static mapFrom(\n error: Error | { message?: string; errorCode?: number },\n ): EmmettError {\n if (error instanceof EmmettError) {\n return error;\n }\n\n return new EmmettError({\n errorCode:\n 'errorCode' in error &&\n error.errorCode !== undefined &&\n error.errorCode !== null\n ? error.errorCode\n : 500,\n message: error.message ?? 'An unknown error occurred',\n });\n }\n}\n\nexport class ConcurrencyError extends EmmettError {\n constructor(\n public current: string | undefined,\n public expected: string,\n message?: string,\n ) {\n super({\n errorCode: 412,\n message:\n message ??\n `Expected version ${expected.toString()} does not match current ${current?.toString()}`,\n });\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, ConcurrencyError.prototype);\n }\n}\n\nexport class ConcurrencyInMemoryDatabaseError extends EmmettError {\n constructor(message?: string) {\n super({\n errorCode: 412,\n message: message ?? `Expected document state does not match current one!`,\n });\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, ConcurrencyInMemoryDatabaseError.prototype);\n }\n}\n\nexport class ValidationError extends EmmettError {\n constructor(message?: string) {\n super({\n errorCode: 400,\n message: message ?? `Validation Error ocurred during Emmett processing`,\n });\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, ValidationError.prototype);\n }\n}\n\nexport class IllegalStateError extends EmmettError {\n constructor(message?: string) {\n super({\n errorCode: 403,\n message: message ?? `Illegal State ocurred during Emmett processing`,\n });\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, IllegalStateError.prototype);\n }\n}\n\nexport class NotFoundError extends EmmettError {\n constructor(options?: { id: string; type: string; message?: string }) {\n super({\n errorCode: 404,\n message:\n options?.message ??\n (options?.id\n ? options.type\n ? `${options.type} with ${options.id} was not found during Emmett processing`\n : `State with ${options.id} was not found during Emmett processing`\n : options?.type\n ? `${options.type} was not found during Emmett processing`\n : 'State was not found during Emmett processing'),\n });\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, NotFoundError.prototype);\n }\n}\n","import { v4 as uuid } from 'uuid';\nimport {\n getInMemoryDatabase,\n type InMemoryDatabase,\n} from '../database/inMemoryDatabase';\nimport type { ProjectionRegistration } from '../projections';\nimport type {\n BigIntStreamPosition,\n CombinedReadEventMetadata,\n Event,\n ReadEvent,\n ReadEventMetadataWithGlobalPosition,\n} from '../typing';\nimport { tryPublishMessagesAfterCommit } from './afterCommit';\nimport {\n type AggregateStreamOptions,\n type AggregateStreamResult,\n type AppendToStreamOptions,\n type AppendToStreamResult,\n type DefaultEventStoreOptions,\n type EventStore,\n type ReadStreamOptions,\n type ReadStreamResult,\n} from './eventStore';\nimport { assertExpectedVersionMatchesCurrent } from './expectedVersion';\nimport { handleInMemoryProjections } from './projections/inMemory';\nimport { StreamingCoordinator } from './subscriptions';\n\nexport const InMemoryEventStoreDefaultStreamVersion = 0n;\n\nexport type InMemoryEventStore =\n EventStore<ReadEventMetadataWithGlobalPosition> & {\n database: InMemoryDatabase;\n };\n\nexport type InMemoryReadEventMetadata = ReadEventMetadataWithGlobalPosition;\n\nexport type InMemoryProjectionHandlerContext = {\n eventStore?: InMemoryEventStore;\n database?: InMemoryDatabase;\n};\n\nexport type InMemoryEventStoreOptions =\n DefaultEventStoreOptions<InMemoryEventStore> & {\n projections?: ProjectionRegistration<\n 'inline',\n InMemoryReadEventMetadata,\n InMemoryProjectionHandlerContext\n >[];\n database?: InMemoryDatabase;\n };\n\nexport type InMemoryReadEvent<EventType extends Event = Event> = ReadEvent<\n EventType,\n ReadEventMetadataWithGlobalPosition\n>;\n\nexport const getInMemoryEventStore = (\n eventStoreOptions?: InMemoryEventStoreOptions,\n): InMemoryEventStore => {\n const streams = new Map<\n string,\n ReadEvent<Event, ReadEventMetadataWithGlobalPosition>[]\n >();\n const streamingCoordinator = StreamingCoordinator();\n\n const getAllEventsCount = () => {\n return Array.from<ReadEvent[]>(streams.values())\n .map((s) => s.length)\n .reduce((p, c) => p + c, 0);\n };\n\n // Get the database instance to be used for projections\n const database = eventStoreOptions?.database || getInMemoryDatabase();\n\n // Extract inline projections from options\n const inlineProjections = (eventStoreOptions?.projections ?? [])\n .filter(({ type }) => type === 'inline')\n .map(({ projection }) => projection);\n\n // Create the event store object\n const eventStore: InMemoryEventStore = {\n database,\n async aggregateStream<State, EventType extends Event>(\n streamName: string,\n options: AggregateStreamOptions<\n State,\n EventType,\n ReadEventMetadataWithGlobalPosition\n >,\n ): Promise<AggregateStreamResult<State>> {\n const { evolve, initialState, read } = options;\n\n const result = await this.readStream<EventType>(streamName, read);\n\n const events = result?.events ?? [];\n\n return {\n currentStreamVersion: BigInt(events.length),\n state: events.reduce(evolve, initialState()),\n streamExists: result.streamExists,\n };\n },\n\n readStream: <EventType extends Event>(\n streamName: string,\n options?: ReadStreamOptions<BigIntStreamPosition>,\n ): Promise<\n ReadStreamResult<EventType, ReadEventMetadataWithGlobalPosition>\n > => {\n const events = streams.get(streamName);\n const currentStreamVersion = events\n ? BigInt(events.length)\n : InMemoryEventStoreDefaultStreamVersion;\n\n assertExpectedVersionMatchesCurrent(\n currentStreamVersion,\n options?.expectedStreamVersion,\n InMemoryEventStoreDefaultStreamVersion,\n );\n\n const from = Number(options && 'from' in options ? options.from : 0);\n const to = Number(\n options && 'to' in options\n ? options.to\n : options && 'maxCount' in options && options.maxCount\n ? options.from + options.maxCount\n : (events?.length ?? 1),\n );\n\n const resultEvents =\n events !== undefined && events.length > 0\n ? events\n .map(\n (e) =>\n e as ReadEvent<\n EventType,\n ReadEventMetadataWithGlobalPosition\n >,\n )\n .slice(from, to)\n : [];\n\n const result: ReadStreamResult<\n EventType,\n ReadEventMetadataWithGlobalPosition\n > = {\n currentStreamVersion,\n events: resultEvents,\n streamExists: events !== undefined && events.length > 0,\n };\n\n return Promise.resolve(result);\n },\n\n appendToStream: async <EventType extends Event>(\n streamName: string,\n events: EventType[],\n options?: AppendToStreamOptions,\n ): Promise<AppendToStreamResult> => {\n const currentEvents = streams.get(streamName) ?? [];\n const currentStreamVersion =\n currentEvents.length > 0\n ? BigInt(currentEvents.length)\n : InMemoryEventStoreDefaultStreamVersion;\n\n assertExpectedVersionMatchesCurrent(\n currentStreamVersion,\n options?.expectedStreamVersion,\n InMemoryEventStoreDefaultStreamVersion,\n );\n\n const newEvents: ReadEvent<\n EventType,\n ReadEventMetadataWithGlobalPosition\n >[] = events.map((event, index) => {\n const metadata: ReadEventMetadataWithGlobalPosition = {\n streamName,\n messageId: uuid(),\n streamPosition: BigInt(currentEvents.length + index + 1),\n globalPosition: BigInt(getAllEventsCount() + index + 1),\n };\n return {\n ...event,\n kind: event.kind ?? 'Event',\n metadata: {\n ...('metadata' in event ? (event.metadata ?? {}) : {}),\n ...metadata,\n } as CombinedReadEventMetadata<\n EventType,\n ReadEventMetadataWithGlobalPosition\n >,\n };\n });\n\n const positionOfLastEventInTheStream = BigInt(\n newEvents.slice(-1)[0]!.metadata.streamPosition,\n );\n\n streams.set(streamName, [...currentEvents, ...newEvents]);\n await streamingCoordinator.notify(newEvents);\n\n // Process projections if there are any registered\n if (inlineProjections.length > 0) {\n await handleInMemoryProjections({\n projections: inlineProjections,\n events: newEvents,\n database: eventStore.database,\n eventStore,\n });\n }\n\n const result: AppendToStreamResult = {\n nextExpectedStreamVersion: positionOfLastEventInTheStream,\n createdNewStream:\n currentStreamVersion === InMemoryEventStoreDefaultStreamVersion,\n };\n\n await tryPublishMessagesAfterCommit<InMemoryEventStore>(\n newEvents,\n eventStoreOptions?.hooks,\n );\n\n return result;\n },\n\n //streamEvents: streamingCoordinator.stream,\n };\n\n return eventStore;\n};\n","import { v7 as uuid } from 'uuid';\nimport { deepEquals } from '../utils';\nimport {\n type DatabaseHandleOptionErrors,\n type DatabaseHandleOptions,\n type DatabaseHandleResult,\n type DeleteResult,\n type Document,\n type DocumentHandler,\n type InsertOneResult,\n type OptionalUnlessRequiredIdAndVersion,\n type ReplaceOneOptions,\n type UpdateResult,\n type WithIdAndVersion,\n type WithoutId,\n} from './types';\nimport { expectedVersionValue, operationResult } from './utils';\n\nexport interface InMemoryDocumentsCollection<T extends Document> {\n handle: (\n id: string,\n handle: DocumentHandler<T>,\n options?: DatabaseHandleOptions,\n ) => Promise<DatabaseHandleResult<T>>;\n findOne: (predicate?: Predicate<T>) => Promise<T | null>;\n find: (predicate?: Predicate<T>) => Promise<T[]>;\n insertOne: (\n document: OptionalUnlessRequiredIdAndVersion<T>,\n ) => Promise<InsertOneResult>;\n deleteOne: (predicate?: Predicate<T>) => Promise<DeleteResult>;\n replaceOne: (\n predicate: Predicate<T>,\n document: WithoutId<T>,\n options?: ReplaceOneOptions,\n ) => Promise<UpdateResult>;\n}\n\nexport interface InMemoryDatabase {\n collection: <T extends Document>(\n name: string,\n ) => InMemoryDocumentsCollection<T>;\n}\n\ntype Predicate<T> = (item: T) => boolean;\ntype CollectionName = string;\n\nexport const getInMemoryDatabase = (): InMemoryDatabase => {\n const storage = new Map<CollectionName, WithIdAndVersion<Document>[]>();\n\n return {\n collection: <T extends Document, CollectionName extends string>(\n collectionName: CollectionName,\n collectionOptions: {\n errors?: DatabaseHandleOptionErrors;\n } = {},\n ): InMemoryDocumentsCollection<T> => {\n const ensureCollectionCreated = () => {\n if (!storage.has(collectionName)) storage.set(collectionName, []);\n };\n\n const errors = collectionOptions.errors;\n\n const collection = {\n collectionName,\n insertOne: async (\n document: OptionalUnlessRequiredIdAndVersion<T>,\n ): Promise<InsertOneResult> => {\n ensureCollectionCreated();\n\n const _id = (document._id as string | undefined | null) ?? uuid();\n const _version = document._version ?? 1n;\n\n const existing = await collection.findOne((c) => c._id === _id);\n\n if (existing) {\n return operationResult<InsertOneResult>(\n {\n successful: false,\n insertedId: null,\n nextExpectedVersion: _version,\n },\n { operationName: 'insertOne', collectionName, errors },\n );\n }\n\n const documentsInCollection = storage.get(collectionName)!;\n const newDocument = { ...document, _id, _version };\n const newCollection = [...documentsInCollection, newDocument];\n storage.set(collectionName, newCollection);\n\n return operationResult<InsertOneResult>(\n {\n successful: true,\n insertedId: _id,\n nextExpectedVersion: _version,\n },\n { operationName: 'insertOne', collectionName, errors },\n );\n },\n findOne: (predicate?: Predicate<T>): Promise<T | null> => {\n ensureCollectionCreated();\n\n const documentsInCollection = storage.get(collectionName);\n const filteredDocuments = predicate\n ? documentsInCollection?.filter((doc) => predicate(doc as T))\n : documentsInCollection;\n\n const firstOne = filteredDocuments?.[0] ?? null;\n\n return Promise.resolve(firstOne as T | null);\n },\n find: (predicate?: Predicate<T>): Promise<T[]> => {\n ensureCollectionCreated();\n\n const documentsInCollection = storage.get(collectionName);\n const filteredDocuments = predicate\n ? documentsInCollection?.filter((doc) => predicate(doc as T))\n : documentsInCollection;\n\n return Promise.resolve(filteredDocuments as T[]);\n },\n deleteOne: (predicate?: Predicate<T>): Promise<DeleteResult> => {\n ensureCollectionCreated();\n\n const documentsInCollection = storage.get(collectionName)!;\n\n if (predicate) {\n const foundIndex = documentsInCollection.findIndex((doc) =>\n predicate(doc as T),\n );\n\n if (foundIndex === -1) {\n return Promise.resolve(\n operationResult<DeleteResult>(\n {\n successful: false,\n matchedCount: 0,\n deletedCount: 0,\n },\n { operationName: 'deleteOne', collectionName, errors },\n ),\n );\n } else {\n const newCollection = documentsInCollection.toSpliced(\n foundIndex,\n 1,\n );\n\n storage.set(collectionName, newCollection);\n\n return Promise.resolve(\n operationResult<DeleteResult>(\n {\n successful: true,\n matchedCount: 1,\n deletedCount: 1,\n },\n { operationName: 'deleteOne', collectionName, errors },\n ),\n );\n }\n }\n\n const newCollection = documentsInCollection.slice(1);\n\n storage.set(collectionName, newCollection);\n\n return Promise.resolve(\n operationResult<DeleteResult>(\n {\n successful: true,\n matchedCount: 1,\n deletedCount: 1,\n },\n { operationName: 'deleteOne', collectionName, errors },\n ),\n );\n },\n replaceOne: (\n predicate: Predicate<T>,\n document: WithoutId<T>,\n options?: ReplaceOneOptions,\n ): Promise<UpdateResult> => {\n ensureCollectionCreated();\n\n const documentsInCollection = storage.get(collectionName)!;\n\n const firstIndex = documentsInCollection.findIndex((doc) =>\n predicate(doc as T),\n );\n\n if (firstIndex === undefined || firstIndex === -1) {\n return Promise.resolve(\n operationResult<UpdateResult>(\n {\n successful: false,\n matchedCount: 0,\n modifiedCount: 0,\n nextExpectedVersion: 0n,\n },\n { operationName: 'replaceOne', collectionName, errors },\n ),\n );\n }\n\n const existing = documentsInCollection[firstIndex]!;\n\n if (\n typeof options?.expectedVersion === 'bigint' &&\n existing._version !== options.expectedVersion\n ) {\n return Promise.resolve(\n operationResult<UpdateResult>(\n {\n successful: false,\n matchedCount: 1,\n modifiedCount: 0,\n nextExpectedVersion: existing._version,\n },\n { operationName: 'replaceOne', collectionName, errors },\n ),\n );\n }\n\n const newVersion = existing._version + 1n;\n\n const newCollection = documentsInCollection.with(firstIndex, {\n _id: existing._id,\n ...document,\n _version: newVersion,\n });\n\n storage.set(collectionName, newCollection);\n\n return Promise.resolve(\n operationResult<UpdateResult>(\n {\n successful: true,\n modifiedCount: 1,\n matchedCount: firstIndex,\n nextExpectedVersion: newVersion,\n },\n { operationName: 'replaceOne', collectionName, errors },\n ),\n );\n },\n handle: async (\n id: string,\n handle: DocumentHandler<T>,\n options?: DatabaseHandleOptions,\n ): Promise<DatabaseHandleResult<T>> => {\n const { expectedVersion: version, ...operationOptions } =\n options ?? {};\n ensureCollectionCreated();\n const existing = await collection.findOne(({ _id }) => _id === id);\n\n const expectedVersion = expectedVersionValue(version);\n\n if (\n (existing == null && version === 'DOCUMENT_EXISTS') ||\n (existing == null && expectedVersion != null) ||\n (existing != null && version === 'DOCUMENT_DOES_NOT_EXIST') ||\n (existing != null &&\n expectedVersion !== null &&\n existing._version !== expectedVersion)\n ) {\n return operationResult<DatabaseHandleResult<T>>(\n {\n successful: false,\n document: existing as WithIdAndVersion<T>,\n },\n { operationName: 'handle', collectionName, errors },\n );\n }\n\n const result = handle(existing !== null ? { ...existing } : null);\n\n if (deepEquals(existing, result))\n return operationResult<DatabaseHandleResult<T>>(\n {\n successful: true,\n document: existing as WithIdAndVersion<T>,\n },\n { operationName: 'handle', collectionName, errors },\n );\n\n if (!existing && result) {\n const newDoc = { ...result, _id: id };\n const insertResult = await collection.insertOne({\n ...newDoc,\n _id: id,\n } as OptionalUnlessRequiredIdAndVersion<T>);\n return {\n ...insertResult,\n document: {\n ...newDoc,\n _version: insertResult.nextExpectedVersion,\n } as unknown as WithIdAndVersion<T>,\n };\n }\n\n if (existing && !result) {\n const deleteResult = await collection.deleteOne(\n ({ _id }) => id === _id,\n );\n return { ...deleteResult, document: null };\n }\n\n if (existing && result) {\n const replaceResult = await collection.replaceOne(\n ({ _id }) => id === _id,\n result,\n {\n ...operationOptions,\n expectedVersion: expectedVersion ?? 'DOCUMENT_EXISTS',\n },\n );\n return {\n ...replaceResult,\n document: {\n ...result,\n _version: replaceResult.nextExpectedVersion,\n } as unknown as WithIdAndVersion<T>,\n };\n }\n\n return operationResult<DatabaseHandleResult<T>>(\n {\n successful: true,\n document: existing as WithIdAndVersion<T>,\n },\n { operationName: 'handle', collectionName, errors },\n );\n },\n };\n\n return collection;\n },\n };\n};\n","import retry from 'async-retry';\nimport { EmmettError } from '../errors';\nimport { JSONParser } from '../serialization';\n\nexport type AsyncRetryOptions<T = unknown> = retry.Options & {\n shouldRetryResult?: (result: T) => boolean;\n shouldRetryError?: (error?: unknown) => boolean;\n};\n\nexport const NoRetries: AsyncRetryOptions = { retries: 0 };\n\nexport const asyncRetry = async <T>(\n fn: () => Promise<T>,\n opts?: AsyncRetryOptions<T>,\n): Promise<T> => {\n if (opts === undefined || opts.retries === 0) return fn();\n\n return retry(\n async (bail) => {\n try {\n const result = await fn();\n\n if (opts?.shouldRetryResult && opts.shouldRetryResult(result)) {\n throw new EmmettError(\n `Retrying because of result: ${JSONParser.stringify(result)}`,\n );\n }\n return result;\n } catch (error) {\n if (opts?.shouldRetryError && !opts.shouldRetryError(error)) {\n bail(error as Error);\n return undefined as unknown as T;\n }\n throw error;\n }\n },\n opts ?? { retries: 0 },\n );\n};\n","import { v4 as uuid } from 'uuid';\nimport {\n handleInMemoryProjections,\n type InMemoryProjectionDefinition,\n} from '.';\nimport {\n getInMemoryDatabase,\n type Document,\n type InMemoryDatabase,\n} from '../../../database';\nimport { isErrorConstructor } from '../../../errors';\nimport { JSONParser } from '../../../serialization';\nimport {\n assertFails,\n AssertionError,\n assertTrue,\n type ThenThrows,\n} from '../../../testing';\nimport type { CombinedReadEventMetadata, ReadEvent } from '../../../typing';\nimport { type Event } from '../../../typing';\nimport type {\n InMemoryEventStore,\n InMemoryReadEventMetadata,\n} from '../../inMemoryEventStore';\n\n// Define a more specific type for T that extends Document\ntype DocumentWithId = Document & { _id?: string | number };\n\nexport type InMemoryProjectionSpecEvent<\n EventType extends Event,\n EventMetaDataType extends\n InMemoryReadEventMetadata = InMemoryReadEventMetadata,\n> = EventType & {\n metadata?: Partial<EventMetaDataType>;\n};\n\nexport type InMemoryProjectionSpecWhenOptions = { numberOfTimes: number };\n\nexport type InMemoryProjectionSpec<EventType extends Event> = (\n givenEvents: InMemoryProjectionSpecEvent<EventType>[],\n) => {\n when: (\n events: InMemoryProjectionSpecEvent<EventType>[],\n options?: InMemoryProjectionSpecWhenOptions,\n ) => {\n then: (assert: InMemoryProjectionAssert, message?: string) => Promise<void>;\n thenThrows: <ErrorType extends Error = Error>(\n ...args: Parameters<ThenThrows<ErrorType>>\n ) => Promise<void>;\n };\n};\n\nexport type InMemoryProjectionAssert = (options: {\n database: InMemoryDatabase;\n}) => Promise<void | boolean>;\n\nexport type InMemoryProjectionSpecOptions<EventType extends Event> = {\n projection: InMemoryProjectionDefinition<EventType>;\n};\n\nexport const InMemoryProjectionSpec = {\n for: <EventType extends Event>(\n options: InMemoryProjectionSpecOptions<EventType>,\n ): InMemoryProjectionSpec<EventType> => {\n const { projection } = options;\n\n return (givenEvents: InMemoryProjectionSpecEvent<EventType>[]) => {\n return {\n when: (\n events: InMemoryProjectionSpecEvent<EventType>[],\n options?: InMemoryProjectionSpecWhenOptions,\n ) => {\n const allEvents: ReadEvent<EventType, InMemoryReadEventMetadata>[] =\n [];\n\n const run = async (database: InMemoryDatabase) => {\n let globalPosition = 0n;\n const numberOfTimes = options?.numberOfTimes ?? 1;\n\n for (const event of [\n ...givenEvents,\n ...Array.from({ length: numberOfTimes }).flatMap(() => events),\n ]) {\n const metadata: InMemoryReadEventMetadata = {\n globalPosition: ++globalPosition,\n streamPosition: globalPosition,\n streamName: event.metadata?.streamName ?? `test-${uuid()}`,\n messageId: uuid(),\n };\n\n allEvents.push({\n ...event,\n kind: 'Event',\n metadata: {\n ...metadata,\n ...('metadata' in event ? (event.metadata ?? {}) : {}),\n } as CombinedReadEventMetadata<\n EventType,\n InMemoryReadEventMetadata\n >,\n });\n }\n\n // Create a minimal mock EventStore implementation\n const mockEventStore = {\n database,\n aggregateStream: async () => {\n return Promise.resolve({\n state: {},\n currentStreamVersion: 0n,\n streamExists: false,\n });\n },\n readStream: async () => {\n return Promise.resolve({\n events: [],\n currentStreamVersion: 0n,\n streamExists: false,\n });\n },\n appendToStream: async () => {\n return Promise.resolve({\n nextExpectedStreamVersion: 0n,\n createdNewStream: false,\n });\n },\n } as InMemoryEventStore;\n\n await handleInMemoryProjections({\n events: allEvents,\n projections: [projection],\n database,\n eventStore: mockEventStore,\n });\n };\n\n return {\n then: async (\n assertFn: InMemoryProjectionAssert,\n message?: string,\n ): Promise<void> => {\n const database = getInMemoryDatabase();\n await run(database);\n\n const succeeded = await assertFn({ database });\n\n if (succeeded !== undefined && succeeded === false) {\n assertFails(\n message ??\n \"Projection specification didn't match the criteria\",\n );\n }\n },\n thenThrows: async <ErrorType extends Error = Error>(\n ...args: Parameters<ThenThrows<ErrorType>>\n ): Promise<void> => {\n const database = getInMemoryDatabase();\n try {\n await run(database);\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 }\n },\n };\n },\n };\n };\n },\n};\n\n// Helper functions for creating events in stream\nexport const eventInStream = <\n EventType extends Event = Event,\n EventMetaDataType extends\n InMemoryReadEventMetadata = InMemoryReadEventMetadata,\n>(\n streamName: string,\n event: InMemoryProjectionSpecEvent<EventType, EventMetaDataType>,\n): InMemoryProjectionSpecEvent<EventType, EventMetaDataType> => {\n return {\n ...event,\n metadata: {\n ...(event.metadata ?? {}),\n streamName: event.metadata?.streamName ?? streamName,\n } as Partial<EventMetaDataType>,\n };\n};\n\nexport const eventsInStream = <\n EventType extends Event = Event,\n EventMetaDataType extends\n InMemoryReadEventMetadata = InMemoryReadEventMetadata,\n>(\n streamName: string,\n events: InMemoryProjectionSpecEvent<EventType, EventMetaDataType>[],\n): InMemoryProjectionSpecEvent<EventType, EventMetaDataType>[] => {\n return events.map((e) => eventInStream(streamName, e));\n};\n\nexport const newEventsInStream = eventsInStream;\n\n// Assertion helpers for checking documents\nexport function documentExists<T extends DocumentWithId>(\n expected: Partial<T>,\n options: { inCollection: string; withId: string | number },\n): InMemoryProjectionAssert {\n return async ({ database }) => {\n const collection = database.collection<T>(options.inCollection);\n\n const document = await collection.findOne((doc) => {\n // Handle both string IDs and numeric IDs in a type-safe way\n const docId = '_id' in doc ? doc._id : undefined;\n return docId === options.withId;\n });\n\n if (!document) {\n assertFails(\n `Document with ID ${options.withId} does not exist in collection ${options.inCollection}`,\n );\n return Promise.resolve(false);\n }\n\n // Check that all expected properties exist with expected values\n for (const [key, value] of Object.entries(expected)) {\n const propKey = key as keyof typeof document;\n if (\n !(key in document) ||\n JSONParser.stringify(document[propKey]) !== JSONParser.stringify(value)\n ) {\n assertFails(`Property ${key} doesn't match the expected value`);\n return Promise.resolve(false);\n }\n }\n\n return Promise.resolve(true);\n };\n}\n\n// Helper for checking document contents\nexport const expectInMemoryDocuments = {\n fromCollection: <T extends DocumentWithId>(collectionName: string) => ({\n withId: (id: string | number) => ({\n toBeEqual: (expected: Partial<T>): InMemoryProjectionAssert =>\n documentExists<T>(expected, {\n inCollection: collectionName,\n withId: id,\n }),\n }),\n }),\n};\n","import { TransformStream } from 'web-streams-polyfill';\nimport type {\n Event,\n ReadEvent,\n ReadEventMetadataWithGlobalPosition,\n} from '../../typing';\nimport { globalStreamCaughtUp, type GlobalSubscriptionEvent } from '../events';\n\nexport const streamTrackingGlobalPosition = (\n currentEvents: ReadEvent<Event, ReadEventMetadataWithGlobalPosition>[],\n) => new CaughtUpTransformStream(currentEvents);\n\nexport class CaughtUpTransformStream extends TransformStream<\n ReadEvent<Event, ReadEventMetadataWithGlobalPosition>,\n | ReadEvent<Event, ReadEventMetadataWithGlobalPosition>\n | GlobalSubscriptionEvent\n> {\n private _currentPosition: bigint;\n private _logPosition: bigint;\n\n constructor(events: ReadEvent<Event, ReadEventMetadataWithGlobalPosition>[]) {\n super({\n start: (controller) => {\n let globalPosition = 0n;\n for (const event of events) {\n controller.enqueue(event);\n globalPosition = event.metadata.globalPosition;\n }\n controller.enqueue(globalStreamCaughtUp({ globalPosition }));\n },\n transform: (event, controller) => {\n this._currentPosition = event.metadata.globalPosition;\n controller.enqueue(event);\n\n if (this._currentPosition < this._logPosition) return;\n\n controller.enqueue(\n globalStreamCaughtUp({ globalPosition: this._currentPosition }),\n );\n },\n });\n\n this._currentPosition = this._logPosition =\n events.length > 0\n ? events[events.length - 1]!.metadata.globalPosition\n : 0n;\n }\n\n public set logPosition(value: bigint) {\n this._logPosition = value;\n }\n}\n","import { v4 as uuid } from 'uuid';\nimport { notifyAboutNoActiveReadersStream } from '../../streaming/transformations/notifyAboutNoActiveReaders';\nimport { writeToStream } from '../../streaming/writers';\nimport type {\n Event,\n ReadEvent,\n ReadEventMetadataWithGlobalPosition,\n} from '../../typing';\nimport {\n CaughtUpTransformStream,\n streamTrackingGlobalPosition,\n} from './caughtUpTransformStream';\n\nexport const StreamingCoordinator = () => {\n const allEvents: ReadEvent<Event, ReadEventMetadataWithGlobalPosition>[] = [];\n const listeners = new Map<string, CaughtUpTransformStream>();\n\n return {\n notify: async (\n events: ReadEvent<Event, ReadEventMetadataWithGlobalPosition>[],\n ) => {\n if (events.length === 0) return;\n\n allEvents.push(...events);\n\n for (const listener of listeners.values()) {\n listener.logPosition =\n events[events.length - 1]!.metadata.globalPosition;\n\n await writeToStream(listener, events);\n }\n },\n\n stream: () => {\n const streamId = uuid();\n const transformStream = streamTrackingGlobalPosition(allEvents);\n\n listeners.set(streamId, transformStream);\n return transformStream.readable.pipeThrough(\n notifyAboutNoActiveReadersStream(\n (stream) => {\n if (listeners.has(stream.streamId))\n listeners.delete(stream.streamId);\n },\n { streamId },\n ),\n );\n },\n };\n};\n","import { v4 as uuid } from 'uuid';\nimport { TransformStream } from 'web-streams-polyfill';\n\nexport const notifyAboutNoActiveReadersStream = <Item>(\n onNoActiveReaderCallback: (\n stream: NotifyAboutNoActiveReadersStream<Item>,\n ) => void,\n options: { streamId?: string; intervalCheckInMs?: number } = {},\n) => new NotifyAboutNoActiveReadersStream(onNoActiveReaderCallback, options);\n\nexport class NotifyAboutNoActiveReadersStream<Item> extends TransformStream<\n Item,\n Item\n> {\n private checkInterval: NodeJS.Timeout | null = null;\n public readonly streamId: string;\n private _isStopped: boolean = false;\n public get hasActiveSubscribers() {\n return !this._isStopped;\n }\n\n constructor(\n private onNoActiveReaderCallback: (\n stream: NotifyAboutNoActiveReadersStream<Item>,\n ) => void,\n options: { streamId?: string; intervalCheckInMs?: number } = {},\n ) {\n super({\n cancel: (reason) => {\n console.log('Stream was canceled. Reason:', reason);\n this.stopChecking();\n },\n });\n this.streamId = options?.streamId ?? uuid();\n\n this.onNoActiveReaderCallback = onNoActiveReaderCallback;\n\n this.startChecking(options?.intervalCheckInMs ?? 20);\n }\n\n private startChecking(interval: number) {\n this.checkInterval = setInterval(() => {\n this.checkNoActiveReader();\n }, interval);\n }\n\n private stopChecking() {\n if (!this.checkInterval) return;\n\n clearInterval(this.checkInterval);\n this.checkInterval = null;\n this._isStopped = true;\n this.onNoActiveReaderCallback(this);\n }\n\n private checkNoActiveReader() {\n if (!this.readable.locked && !this._isStopped) {\n this.stopChecking();\n }\n }\n}\n","import { ReadableStream } from 'web-streams-polyfill';\n\nexport const fromArray = <T>(chunks: T[]) =>\n new ReadableStream<T>({\n start(controller) {\n for (const chunk of chunks) controller.enqueue(chunk);\n controller.close();\n },\n });\n","import {\n type ReadableStream,\n type ReadableStreamDefaultReadResult,\n type TransformStreamDefaultController,\n} from 'web-streams-polyfill';\nimport type { AsyncRetryOptions } from '../utils';\nimport type { Decoder } from './decoders';\nimport { DefaultDecoder } from './decoders/composite';\nimport { streamTransformations } from './transformations';\n\nconst { retry } = streamTransformations;\n\nexport const restream = <\n Source = unknown,\n Transformed = Source,\n StreamType = Source,\n>(\n createSourceStream: () => ReadableStream<StreamType>,\n transform: (input: Source) => Transformed = (source) =>\n source as unknown as Transformed,\n retryOptions: AsyncRetryOptions = { forever: true, minTimeout: 25 },\n decoder: Decoder<StreamType, Source> = new DefaultDecoder<Source>(),\n): ReadableStream<Transformed> =>\n retry(createSourceStream, handleChunk(transform, decoder), retryOptions)\n .readable;\n\nconst handleChunk =\n <Source = unknown, Transformed = Source, StreamType = Source>(\n transform: (input: Source) => Transformed = (source) =>\n source as unknown as Transformed,\n decoder: Decoder<StreamType, Source> = new DefaultDecoder<Source>(),\n ) =>\n (\n readResult: ReadableStreamDefaultReadResult<StreamType>,\n controller: TransformStreamDefaultController<Transformed>,\n ): void => {\n const { done: isDone, value } = readResult;\n\n if (value) decoder.addToBuffer(value);\n\n if (!isDone && !decoder.hasCompleteMessage()) return;\n\n decodeAndTransform(decoder, transform, controller);\n };\n\nconst decodeAndTransform = <StreamType, Source, Transformed = Source>(\n decoder: Decoder<StreamType, Source>,\n transform: (input: Source) => Transformed,\n controller: TransformStreamDefaultController<Transformed>,\n) => {\n try {\n const decoded = decoder.decode();\n if (!decoded) return; // TODO: Add a proper handling of decode errors\n\n const transformed = transform(decoded);\n controller.enqueue(transformed);\n } catch (error) {\n controller.error(new Error(`Decoding error: ${error?.toString()}`));\n }\n};\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const filter = <Item>(filter: (item: Item) => boolean) =>\n new TransformStream<Item, Item>({\n transform(chunk, controller) {\n if (filter(chunk)) {\n controller.enqueue(chunk);\n }\n },\n });\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const map = <From, To>(map: (item: From) => To) =>\n new TransformStream<From, To>({\n transform(chunk, controller) {\n controller.enqueue(map(chunk));\n },\n });\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const reduce = <I, O>(\n reducer: (accumulator: O, chunk: I) => O,\n initialValue: O,\n) => new ReduceTransformStream<I, O>(reducer, initialValue);\n\nexport class ReduceTransformStream<I, O> extends TransformStream<I, O> {\n private accumulator: O;\n private reducer: (accumulator: O, chunk: I) => O;\n\n constructor(reducer: (accumulator: O, chunk: I) => O, initialValue: O) {\n super({\n transform: (chunk) => {\n this.accumulator = this.reducer(this.accumulator, chunk);\n },\n flush: (controller) => {\n controller.enqueue(this.accumulator);\n controller.terminate();\n },\n });\n\n this.accumulator = initialValue;\n this.reducer = reducer;\n }\n}\n","import {\n type ReadableStream,\n type ReadableStreamDefaultReadResult,\n TransformStream,\n type TransformStreamDefaultController,\n} from 'web-streams-polyfill';\nimport { type AsyncRetryOptions, asyncRetry } from '../../utils';\n\nexport const retryStream = <\n Source = unknown,\n Transformed = Source,\n StreamType = Source,\n>(\n createSourceStream: () => ReadableStream<StreamType>,\n handleChunk: (\n readResult: ReadableStreamDefaultReadResult<StreamType>,\n controller: TransformStreamDefaultController<Transformed>,\n ) => Promise<void> | void,\n retryOptions: AsyncRetryOptions = { forever: true, minTimeout: 25 },\n): TransformStream<Source, Transformed> =>\n new TransformStream<Source, Transformed>({\n start(controller) {\n asyncRetry(\n () => onRestream(createSourceStream, handleChunk, controller),\n retryOptions,\n ).catch((error) => {\n controller.error(error);\n });\n },\n });\n\nconst onRestream = async <StreamType, Source, Transformed = Source>(\n createSourceStream: () => ReadableStream<StreamType>,\n handleChunk: (\n readResult: ReadableStreamDefaultReadResult<StreamType>,\n controller: TransformStreamDefaultController<Transformed>,\n ) => Promise<void> | void,\n controller: TransformStreamDefaultController<Transformed>,\n): Promise<void> => {\n const sourceStream = createSourceStream();\n const reader = sourceStream.getReader();\n\n try {\n let done: boolean;\n\n do {\n const result = await reader.read();\n done = result.done;\n\n await handleChunk(result, controller);\n\n if (done) {\n controller.terminate();\n }\n } while (!done);\n } finally {\n reader.releaseLock();\n }\n};\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const skip = <T>(limit: number) => new SkipTransformStream<T>(limit);\n\nexport class SkipTransformStream<T> extends TransformStream<T, T> {\n private count = 0;\n private skip: number;\n\n constructor(skip: number) {\n super({\n transform: (chunk, controller) => {\n this.count++;\n if (this.count > this.skip) {\n controller.enqueue(chunk);\n }\n },\n });\n\n this.skip = skip;\n }\n}\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const stopAfter = <Item>(stopCondition: (item: Item) => boolean) =>\n new TransformStream<Item, Item>({\n transform(chunk, controller) {\n controller.enqueue(chunk);\n\n if (stopCondition(chunk)) {\n controller.terminate();\n }\n },\n });\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const stopOn = <Item>(stopCondition: (item: Item) => boolean) =>\n new TransformStream<Item, Item>({\n async transform(chunk, controller) {\n if (!stopCondition(chunk)) {\n controller.enqueue(chunk);\n return;\n }\n await Promise.resolve();\n controller.terminate();\n },\n });\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const take = <T>(limit: number) => new TakeTransformStream<T>(limit);\n\nexport class TakeTransformStream<T> extends TransformStream<T, T> {\n private count = 0;\n private limit: number;\n\n constructor(limit: number) {\n super({\n transform: (chunk, controller) => {\n if (this.count < this.limit) {\n this.count++;\n controller.enqueue(chunk);\n } else {\n controller.terminate();\n }\n },\n });\n\n this.limit = limit;\n }\n}\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const waitAtMost = <Item>(waitTimeInMs: number) =>\n new TransformStream<Item, Item>({\n start(controller) {\n const timeoutId = setTimeout(() => {\n controller.terminate();\n }, waitTimeInMs);\n\n const originalTerminate = controller.terminate.bind(controller);\n\n // Clear the timeout if the stream is terminated early\n controller.terminate = () => {\n clearTimeout(timeoutId);\n originalTerminate();\n };\n },\n transform(chunk, controller) {\n controller.enqueue(chunk);\n },\n });\n","import { ConcurrencyError } from '../errors';\nimport type { BigIntStreamPosition, Flavour } from '../typing';\n\nexport type ExpectedStreamVersion<VersionType = BigIntStreamPosition> =\n | ExpectedStreamVersionWithValue<VersionType>\n | ExpectedStreamVersionGeneral;\n\nexport type ExpectedStreamVersionWithValue<VersionType = BigIntStreamPosition> =\n Flavour<VersionType, 'StreamVersion'>;\n\nexport type ExpectedStreamVersionGeneral = Flavour<\n 'STREAM_EXISTS' | 'STREAM_DOES_NOT_EXIST' | 'NO_CONCURRENCY_CHECK',\n 'StreamVersion'\n>;\n\nexport const STREAM_EXISTS = 'STREAM_EXISTS' as ExpectedStreamVersionGeneral;\nexport const STREAM_DOES_NOT_EXIST =\n 'STREAM_DOES_NOT_EXIST' as ExpectedStreamVersionGeneral;\nexport const NO_CONCURRENCY_CHECK =\n 'NO_CONCURRENCY_CHECK' as ExpectedStreamVersionGeneral;\n\nexport const matchesExpectedVersion = <StreamVersion = BigIntStreamPosition>(\n current: StreamVersion | undefined,\n expected: ExpectedStreamVersion<StreamVersion>,\n defaultVersion: StreamVersion,\n): boolean => {\n if (expected === NO_CONCURRENCY_CHECK) return true;\n\n if (expected == STREAM_DOES_NOT_EXIST) return current === defaultVersion;\n\n if (expected == STREAM_EXISTS) return current !== defaultVersion;\n\n return current === expected;\n};\n\nexport const assertExpectedVersionMatchesCurrent = <\n StreamVersion = BigIntStreamPosition,\n>(\n current: StreamVersion,\n expected: ExpectedStreamVersion<StreamVersion> | undefined,\n defaultVersion: StreamVersion,\n): void => {\n expected ??= NO_CONCURRENCY_CHECK;\n\n if (!matchesExpectedVersion(current, expected, defaultVersion))\n throw new ExpectedVersionConflictError(current, expected);\n};\n\nexport class ExpectedVersionConflictError<\n VersionType = BigIntStreamPosition,\n> extends ConcurrencyError {\n constructor(\n current: VersionType,\n expected: ExpectedStreamVersion<VersionType>,\n ) {\n super(current?.toString(), expected?.toString());\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, ExpectedVersionConflictError.prototype);\n }\n}\n\nexport const isExpectedVersionConflictError = (\n error: unknown,\n): error is ExpectedVersionConflictError =>\n error instanceof ExpectedVersionConflictError;\n","const isPrimitive = (value: unknown): boolean => {\n const type = typeof value;\n return (\n value === null ||\n value === undefined ||\n type === 'boolean' ||\n type === 'number' ||\n type === 'string' ||\n type === 'symbol' ||\n type === 'bigint'\n );\n};\n\nconst compareArrays = <T>(left: T[], right: T[]): boolean => {\n if (left.length !== right.length) {\n return false;\n }\n for (let i = 0; i < left.length; i++) {\n const leftHas = i in left;\n const rightHas = i in right;\n if (leftHas !== rightHas) return false;\n if (leftHas && !deepEquals(left[i], right[i])) return false;\n }\n return true;\n};\n\nconst compareDates = (left: Date, right: Date): boolean => {\n return left.getTime() === right.getTime();\n};\n\nconst compareRegExps = (left: RegExp, right: RegExp): boolean => {\n return left.toString() === right.toString();\n};\n\nconst compareErrors = (left: Error, right: Error): boolean => {\n if (left.message !== right.message || left.name !== right.name) {\n return false;\n }\n const leftKeys = Object.keys(left);\n const rightKeys = Object.keys(right);\n if (leftKeys.length !== rightKeys.length) return false;\n const rightKeySet = new Set(rightKeys);\n for (const key of leftKeys) {\n if (!rightKeySet.has(key)) return false;\n // @ts-expect-error - accessing dynamic keys\n if (!deepEquals(left[key], right[key])) return false;\n }\n return true;\n};\n\nconst compareMaps = (\n left: Map<unknown, unknown>,\n right: Map<unknown, unknown>,\n): boolean => {\n if (left.size !== right.size) return false;\n\n for (const [key, value] of left) {\n if (isPrimitive(key)) {\n if (!right.has(key) || !deepEquals(value, right.get(key))) {\n return false;\n }\n } else {\n let found = false;\n for (const [rightKey, rightValue] of right) {\n if (deepEquals(key, rightKey) && deepEquals(value, rightValue)) {\n found = true;\n break;\n }\n }\n if (!found) return false;\n }\n }\n return true;\n};\n\nconst compareSets = (left: Set<unknown>, right: Set<unknown>): boolean => {\n if (left.size !== right.size) return false;\n\n for (const leftItem of left) {\n if (isPrimitive(leftItem)) {\n if (!right.has(leftItem)) return false;\n } else {\n let found = false;\n for (const rightItem of right) {\n if (deepEquals(leftItem, rightItem)) {\n found = true;\n break;\n }\n }\n if (!found) return false;\n }\n }\n return true;\n};\n\nconst compareArrayBuffers = (\n left: ArrayBuffer,\n right: ArrayBuffer,\n): boolean => {\n if (left.byteLength !== right.byteLength) return false;\n const leftView = new Uint8Array(left);\n const rightView = new Uint8Array(right);\n for (let i = 0; i < leftView.length; i++) {\n if (leftView[i] !== rightView[i]) return false;\n }\n return true;\n};\n\nconst compareTypedArrays = (\n left: ArrayBufferView,\n right: ArrayBufferView,\n): boolean => {\n if (left.constructor !== right.constructor) return false;\n if (left.byteLength !== right.byteLength) return false;\n\n const leftArray = new Uint8Array(\n left.buffer,\n left.byteOffset,\n left.byteLength,\n );\n const rightArray = new Uint8Array(\n right.buffer,\n right.byteOffset,\n right.byteLength,\n );\n\n for (let i = 0; i < leftArray.length; i++) {\n if (leftArray[i] !== rightArray[i]) return false;\n }\n return true;\n};\n\nconst compareObjects = (\n left: Record<string, unknown>,\n right: Record<string, unknown>,\n): boolean => {\n const keys1 = Object.keys(left);\n const keys2 = Object.keys(right);\n\n if (keys1.length !== keys2.length) {\n return false;\n }\n\n for (const key of keys1) {\n if (left[key] instanceof Function && right[key] instanceof Function) {\n continue;\n }\n\n const isEqual = deepEquals(left[key], right[key]);\n if (!isEqual) {\n return false;\n }\n }\n\n return true;\n};\n\nconst getType = (value: unknown): string => {\n if (value === null) return 'null';\n if (value === undefined) return 'undefined';\n\n const primitiveType = typeof value;\n if (primitiveType !== 'object') return primitiveType;\n\n if (Array.isArray(value)) return 'array';\n if (value instanceof Boolean) return 'boxed-boolean';\n if (value instanceof Number) return 'boxed-number';\n if (value instanceof String) return 'boxed-string';\n if (value instanceof Date) return 'date';\n if (value instanceof RegExp) return 'regexp';\n if (value instanceof Error) return 'error';\n if (value instanceof Map) return 'map';\n if (value instanceof Set) return 'set';\n if (value instanceof ArrayBuffer) return 'arraybuffer';\n if (value instanceof DataView) return 'dataview';\n if (value instanceof WeakMap) return 'weakmap';\n if (value instanceof WeakSet) return 'weakset';\n\n if (ArrayBuffer.isView(value)) return 'typedarray';\n\n return 'object';\n};\n\nexport const deepEquals = <T>(left: T, right: T): boolean => {\n if (left === right) return true;\n\n if (isEquatable(left)) {\n return left.equals(right);\n }\n\n const leftType = getType(left);\n const rightType = getType(right);\n\n if (leftType !== rightType) return false;\n\n switch (leftType) {\n case 'null':\n case 'undefined':\n case 'boolean':\n case 'number':\n case 'bigint':\n case 'string':\n case 'symbol':\n case 'function':\n return left === right;\n\n case 'array':\n return compareArrays(left as unknown[], right as unknown[]);\n\n case 'date':\n return compareDates(left as Date, right as Date);\n\n case 'regexp':\n return compareRegExps(left as RegExp, right as RegExp);\n\n case 'error':\n return compareErrors(left as Error, right as Error);\n\n case 'map':\n return compareMaps(\n left as Map<unknown, unknown>,\n right as Map<unknown, unknown>,\n );\n\n case 'set':\n return compareSets(left as Set<unknown>, right as Set<unknown>);\n\n case 'arraybuffer':\n return compareArrayBuffers(left as ArrayBuffer, right as ArrayBuffer);\n\n case 'dataview':\n case 'weakmap':\n case 'weakset':\n return false;\n\n case 'typedarray':\n return compareTypedArrays(\n left as ArrayBufferView,\n right as ArrayBufferView,\n );\n\n case 'boxed-boolean':\n return (left as boolean).valueOf() === (right as boolean).valueOf();\n\n case 'boxed-number':\n return (left as number).valueOf() === (right as number).valueOf();\n\n case 'boxed-string':\n return (left as string).valueOf() === (right as string).valueOf();\n\n case 'object':\n return compareObjects(\n left as Record<string, unknown>,\n right as Record<string, unknown>,\n );\n\n default:\n return false;\n }\n};\n\nexport type Equatable<T> = { equals: (right: T) => boolean } & T;\n\nexport const isEquatable = <T>(left: T): left is Equatable<T> => {\n return (\n left !== null &&\n left !== undefined &&\n typeof left === 'object' &&\n 'equals' in left &&\n typeof left['equals'] === 'function'\n );\n};\n","export class ParseError extends Error {\n constructor(text: string) {\n super(`Cannot parse! ${text}`);\n }\n}\n\nexport type Mapper<From, To = From> =\n | ((value: unknown) => To)\n | ((value: Partial<From>) => To)\n | ((value: From) => To)\n | ((value: Partial<To>) => To)\n | ((value: To) => To)\n | ((value: Partial<To | From>) => To)\n | ((value: To | From) => To);\n\nexport type MapperArgs<From, To = From> = Partial<From> &\n From &\n Partial<To> &\n To;\n\nexport type ParseOptions<From, To = From> = {\n reviver?: (key: string, value: unknown) => unknown;\n map?: Mapper<From, To>;\n typeCheck?: <To>(value: unknown) => value is To;\n};\n\nexport type StringifyOptions<From, To = From> = {\n map?: Mapper<From, To>;\n};\n\nexport const JSONParser = {\n stringify: <From, To = From>(\n value: From,\n options?: StringifyOptions<From, To>,\n ) => {\n return JSON.stringify(\n options?.map ? options.map(value as MapperArgs<From, To>) : value,\n //TODO: Consider adding support to DateTime and adding specific format to mark that's a bigint\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n (_, v) => (typeof v === 'bigint' ? v.toString() : v),\n );\n },\n parse: <From, To = From>(\n text: string,\n options?: ParseOptions<From, To>,\n ): To | undefined => {\n const parsed: unknown = JSON.parse(text, options?.reviver);\n\n if (options?.typeCheck && !options?.typeCheck<To>(parsed))\n throw new ParseError(text);\n\n return options?.map\n ? options.map(parsed as MapperArgs<From, To>)\n : (parsed as To | undefined);\n },\n};\n","import { JSONParser } from '../serialization';\nimport type { DefaultRecord } from '../typing';\nimport { deepEquals } from '../utils';\n\nexport class AssertionError extends Error {\n constructor(message: string) {\n super(message);\n }\n}\n\nexport const isSubset = (superObj: unknown, subObj: unknown): boolean => {\n const sup = superObj as DefaultRecord;\n const sub = subObj as DefaultRecord;\n\n assertOk(sup);\n assertOk(sub);\n\n return Object.keys(sub).every((ele: string) => {\n if (typeof sub[ele] == 'object') {\n return isSubset(sup[ele], sub[ele]);\n }\n return sub[ele] === sup[ele];\n });\n};\n\nexport const assertFails = (message?: string) => {\n throw new AssertionError(message ?? 'That should not ever happened, right?');\n};\n\nexport const assertThrowsAsync = async <TError extends Error>(\n fun: () => Promise<void>,\n errorCheck?: (error: Error) => boolean,\n): Promise<TError> => {\n try {\n await fun();\n } catch (error) {\n const typedError = error as TError;\n if (typedError instanceof AssertionError || !errorCheck) {\n assertFalse(\n typedError instanceof AssertionError,\n \"Function didn't throw expected error\",\n );\n return typedError;\n }\n\n assertTrue(\n errorCheck(typedError),\n `Error doesn't match the expected condition: ${JSONParser.stringify(error)}`,\n );\n\n return typedError;\n }\n throw new AssertionError(\"Function didn't throw expected error\");\n};\n\nexport const assertThrows = <TError extends Error>(\n fun: () => void,\n errorCheck?: (error: Error) => boolean,\n): TError => {\n try {\n fun();\n } catch (error) {\n const typedError = error as TError;\n\n if (errorCheck) {\n assertTrue(\n errorCheck(typedError),\n `Error doesn't match the expected condition: ${JSONParser.stringify(error)}`,\n );\n } else if (typedError instanceof AssertionError) {\n assertFalse(\n typedError instanceof AssertionError,\n \"Function didn't throw expected error\",\n );\n }\n\n return typedError;\n }\n throw new AssertionError(\"Function didn't throw expected error\");\n};\n\nexport const assertDoesNotThrow = <TError extends Error>(\n fun: () => void,\n errorCheck?: (error: Error) => boolean,\n): TError | null => {\n try {\n fun();\n return null;\n } catch (error) {\n const typedError = error as TError;\n\n if (errorCheck) {\n assertFalse(\n errorCheck(typedError),\n `Error matching the expected condition was thrown!: ${JSONParser.stringify(error)}`,\n );\n } else {\n assertFails(`Function threw an error: ${JSONParser.stringify(error)}`);\n }\n\n return typedError;\n }\n};\n\nexport const assertRejects = async <T, TError extends Error = Error>(\n promise: Promise<T>,\n errorCheck?: ((error: TError) => boolean) | TError,\n) => {\n try {\n await promise;\n throw new AssertionError(\"Function didn't throw expected error\");\n } catch (error) {\n if (!errorCheck) return;\n\n if (errorCheck instanceof Error) assertDeepEqual(error, errorCheck);\n else assertTrue(errorCheck(error as TError));\n }\n};\n\nexport const assertMatches = (\n actual: unknown,\n expected: unknown,\n message?: string,\n) => {\n if (!isSubset(actual, expected))\n throw new AssertionError(\n message ??\n `subObj:\\n${JSONParser.stringify(expected)}\\nis not subset of\\n${JSONParser.stringify(actual)}`,\n );\n};\n\nexport const assertDeepEqual = <T = unknown>(\n actual: T,\n expected: T,\n message?: string,\n) => {\n if (!deepEquals(actual, expected))\n throw new AssertionError(\n message ??\n `subObj:\\n${JSONParser.stringify(expected)}\\nis not equal to\\n${JSONParser.stringify(actual)}`,\n );\n};\n\nexport const assertNotDeepEqual = <T = unknown>(\n actual: T,\n expected: T,\n message?: string,\n) => {\n if (deepEquals(actual, expected))\n throw new AssertionError(\n message ??\n `subObj:\\n${JSONParser.stringify(expected)}\\nis equals to\\n${JSONParser.stringify(actual)}`,\n );\n};\n\nexport const assertThat = <T>(item: T) => {\n return {\n isEqualTo: (other: T) => assertTrue(deepEquals(item, other)),\n };\n};\n\nexport function assertFalse(\n condition: boolean,\n message?: string,\n): asserts condition is false {\n if (condition !== false)\n throw new AssertionError(message ?? `Condition is true`);\n}\n\nexport function assertTrue(\n condition: boolean,\n message?: string,\n): asserts condition is true {\n if (condition !== true)\n throw new AssertionError(message ?? `Condition is false`);\n}\n\nexport function assertOk<T>(\n obj: T | null | undefined,\n message?: string,\n): asserts obj is T {\n if (!obj) throw new AssertionError(message ?? `Condition is not truthy`);\n}\n\nexport function assertEqual<T>(\n expected: T | null | undefined,\n actual: T | null | undefined,\n message?: string,\n): void {\n if (expected !== actual)\n throw new AssertionError(\n `${message ?? 'Objects are not equal'}:\\nExpected: ${JSONParser.stringify(expected)}\\nActual: ${JSONParser.stringify(actual)}`,\n );\n}\n\nexport function assertNotEqual<T>(\n obj: T | null | undefined,\n other: T | null | undefined,\n message?: string,\n): void {\n if (obj === other)\n throw new AssertionError(\n message ?? `Objects are equal: ${JSONParser.stringify(obj)}`,\n );\n}\n\nexport function assertIsNotNull<T extends object | bigint>(\n result: T | null,\n): asserts result is T {\n assertNotEqual(result, null);\n assertOk(result);\n}\n\nexport function assertIsNull<T extends object>(\n result: T | null,\n): asserts result is null {\n assertEqual(result, null);\n}\n\ntype Call = {\n arguments: unknown[];\n result: unknown;\n target: unknown;\n this: unknown;\n};\n\nexport type ArgumentMatcher = (arg: unknown) => boolean;\n\nexport const argValue =\n <T>(value: T): ArgumentMatcher =>\n (arg) =>\n deepEquals(arg, value);\n\nexport const argMatches =\n <T>(matches: (arg: T) => boolean): ArgumentMatcher =>\n (arg) =>\n matches(arg as T);\n\n// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\nexport type MockedFunction = Function & { mock?: { calls: Call[] } };\n\nexport function verifyThat(fn: MockedFunction) {\n return {\n calledTimes: (times: number) => {\n assertEqual(fn.mock?.calls?.length, times);\n },\n notCalled: () => {\n assertEqual(fn?.mock?.calls?.length, 0);\n },\n called: () => {\n assertTrue(\n fn.mock?.calls.length !== undefined && fn.mock.calls.length > 0,\n );\n },\n calledWith: (...args: unknown[]) => {\n assertTrue(\n fn.mock?.calls.length !== undefined &&\n fn.mock.calls.length >= 1 &&\n fn.mock.calls.some((call) => deepEquals(call.arguments, args)),\n );\n },\n calledOnceWith: (...args: unknown[]) => {\n assertTrue(\n fn.mock?.calls.length !== undefined &&\n fn.mock.calls.length === 1 &&\n fn.mock.calls.some((call) => deepEquals(call.arguments, args)),\n );\n },\n calledWithArgumentMatching: (...matches: ArgumentMatcher[]) => {\n assertTrue(\n fn.mock?.calls.length !== undefined && fn.mock.calls.length >= 1,\n );\n assertTrue(\n fn.mock?.calls.length !== undefined &&\n fn.mock.calls.length >= 1 &&\n fn.mock.calls.some(\n (call) =>\n call.arguments &&\n call.arguments.length >= matches.length &&\n matches.every((match, index) => match(call.arguments[index])),\n ),\n );\n },\n notCalledWithArgumentMatching: (...matches: ArgumentMatcher[]) => {\n assertFalse(\n fn.mock?.calls.length !== undefined &&\n fn.mock.calls.length >= 1 &&\n fn.mock.calls[0]!.arguments &&\n fn.mock.calls[0]!.arguments.length >= matches.length &&\n matches.every((match, index) =>\n match(fn.mock!.calls[0]!.arguments[index]),\n ),\n );\n },\n };\n}\n\nexport const assertThatArray = <T>(array: T[]) => {\n return {\n isEmpty: () =>\n assertEqual(\n array.length,\n 0,\n `Array is not empty ${JSONParser.stringify(array)}`,\n ),\n isNotEmpty: () => assertNotEqual(array.length, 0, `Array is empty`),\n hasSize: (length: number) => assertEqual(array.length, length),\n containsElements: (other: T[]) => {\n assertTrue(other.every((ts) => array.some((o) => deepEquals(ts, o))));\n },\n containsElementsMatching: (other: T[]) => {\n assertTrue(other.every((ts) => array.some((o) => isSubset(o, ts))));\n },\n containsOnlyElementsMatching: (other: T[]) => {\n assertEqual(array.length, other.length, `Arrays lengths don't match`);\n assertTrue(other.every((ts) => array.some((o) => isSubset(o, ts))));\n },\n containsExactlyInAnyOrder: (other: T[]) => {\n assertEqual(array.length, other.length);\n assertTrue(array.every((ts) => other.some((o) => deepEquals(ts, o))));\n },\n containsExactlyInAnyOrderElementsOf: (other: T[]) => {\n assertEqual(array.length, other.length);\n assertTrue(array.every((ts) => other.some((o) => deepEquals(ts, o))));\n },\n containsExactlyElementsOf: (other: T[]) => {\n assertEqual(array.length, other.length);\n for (let i = 0; i < array.length; i++) {\n assertTrue(deepEquals(array[i], other[i]));\n }\n },\n containsExactly: (elem: T) => {\n assertEqual(array.length, 1);\n assertTrue(deepEquals(array[0], elem));\n },\n contains: (elem: T) => {\n assertTrue(array.some((a) => deepEquals(a, elem)));\n },\n containsOnlyOnceElementsOf: (other: T[]) => {\n assertTrue(\n other\n .map((o) => array.filter((a) => deepEquals(a, o)).length)\n .filter((a) => a === 1).length === other.length,\n );\n },\n containsAnyOf: (other: T[]) => {\n assertTrue(array.some((a) => other.some((o) => deepEquals(a, o))));\n },\n allMatch: (matches: (item: T) => boolean) => {\n assertTrue(array.every(matches));\n },\n anyMatches: (matches: (item: T) => boolean) => {\n assertTrue(array.some(matches));\n },\n allMatchAsync: async (\n matches: (item: T) => Promise<boolean>,\n ): Promise<void> => {\n for (const item of array) {\n assertTrue(await matches(item));\n }\n },\n };\n};\n","import type { EmmettError } from '../errors';\nimport type { ProjectionDefinition } from '../projections';\nimport {\n type AnyEvent,\n type AnyMessage,\n type AnyReadEventMetadata,\n type AnyRecordedMessageMetadata,\n type BatchRecordedMessageHandlerWithContext,\n type CanHandle,\n type DefaultRecord,\n type Event,\n type GlobalPositionTypeOfRecordedMessageMetadata,\n type Message,\n type MessageHandlerResult,\n type RecordedMessage,\n type SingleMessageHandlerWithContext,\n type SingleRecordedMessageHandlerWithContext,\n} from '../typing';\nimport { isBigint } from '../validation';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type CurrentMessageProcessorPosition<CheckpointType = any> =\n | { lastCheckpoint: CheckpointType }\n | 'BEGINNING'\n | 'END';\n\nexport type GetCheckpoint<\n MessageType extends AnyMessage = AnyMessage,\n MessageMetadataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n CheckpointType = GlobalPositionTypeOfRecordedMessageMetadata<MessageMetadataType>,\n> = (\n message: RecordedMessage<MessageType, MessageMetadataType>,\n) => CheckpointType | null;\n\nexport const getCheckpoint = <\n MessageType extends AnyMessage = AnyMessage,\n MessageMetadataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n CheckpointType = GlobalPositionTypeOfRecordedMessageMetadata<MessageMetadataType>,\n>(\n message: RecordedMessage<MessageType, MessageMetadataType>,\n): CheckpointType | null => {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return 'checkpoint' in message.metadata &&\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n isBigint(message.metadata.checkpoint)\n ? // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n message.metadata.checkpoint\n : 'globalPosition' in message.metadata &&\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n isBigint(message.metadata.globalPosition)\n ? // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n message.metadata.globalPosition\n : 'streamPosition' in message.metadata &&\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n isBigint(message.metadata.streamPosition)\n ? // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n message.metadata.streamPosition\n : null;\n};\n\nexport const wasMessageHandled = <\n MessageType extends AnyMessage = AnyMessage,\n MessageMetadataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n CheckpointType = GlobalPositionTypeOfRecordedMessageMetadata<MessageMetadataType>,\n>(\n message: RecordedMessage<MessageType, MessageMetadataType>,\n checkpoint: CheckpointType | null,\n): boolean => {\n //TODO Make it smarter\n const messageCheckpoint = getCheckpoint(message);\n const checkpointBigint = checkpoint as bigint | null;\n\n return (\n messageCheckpoint !== null &&\n messageCheckpoint !== undefined &&\n checkpointBigint !== null &&\n checkpointBigint !== undefined &&\n messageCheckpoint <= checkpointBigint\n );\n};\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type MessageProcessorStartFrom<CheckpointType = any> =\n | CurrentMessageProcessorPosition<CheckpointType>\n | 'CURRENT';\n\nexport type MessageProcessorType = 'projector' | 'reactor';\nexport const MessageProcessorType = {\n PROJECTOR: 'projector' as MessageProcessorType,\n REACTOR: 'reactor' as MessageProcessorType,\n};\n\nexport type MessageProcessor<\n MessageType extends AnyMessage = AnyMessage,\n MessageMetadataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n HandlerContext extends DefaultRecord | undefined = undefined,\n CheckpointType = GlobalPositionTypeOfRecordedMessageMetadata<MessageMetadataType>,\n> = {\n id: string;\n type: string;\n start: (\n options: Partial<HandlerContext>,\n ) => Promise<CurrentMessageProcessorPosition<CheckpointType> | undefined>;\n close: () => Promise<void>;\n isActive: boolean;\n handle: BatchRecordedMessageHandlerWithContext<\n MessageType,\n MessageMetadataType,\n Partial<HandlerContext>\n >;\n};\n\nexport const MessageProcessor = {\n result: {\n skip: (options?: { reason?: string }): MessageHandlerResult => ({\n type: 'SKIP',\n ...(options ?? {}),\n }),\n stop: (options?: {\n reason?: string;\n error?: EmmettError;\n }): MessageHandlerResult => ({\n type: 'STOP',\n ...(options ?? {}),\n }),\n },\n};\n\nexport type MessageProcessingScope<\n HandlerContext extends DefaultRecord | undefined = undefined,\n> = <Result = MessageHandlerResult>(\n handler: (context: HandlerContext) => Result | Promise<Result>,\n partialContext: Partial<HandlerContext>,\n) => Result | Promise<Result>;\n\nexport type Checkpointer<\n MessageType extends AnyMessage = AnyMessage,\n MessageMetadataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n HandlerContext extends DefaultRecord = DefaultRecord,\n CheckpointType = GlobalPositionTypeOfRecordedMessageMetadata<MessageMetadataType>,\n> = {\n read: ReadProcessorCheckpoint<CheckpointType, HandlerContext>;\n store: StoreProcessorCheckpoint<\n MessageType,\n MessageMetadataType,\n CheckpointType,\n HandlerContext\n >;\n};\n\nexport type BaseMessageProcessorOptions<\n MessageType extends AnyMessage = AnyMessage,\n MessageMetadataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n HandlerContext extends DefaultRecord = DefaultRecord,\n CheckpointType = GlobalPositionTypeOfRecordedMessageMetadata<MessageMetadataType>,\n> = {\n type?: string;\n processorId: string;\n version?: number;\n partition?: string;\n startFrom?: MessageProcessorStartFrom<CheckpointType>;\n stopAfter?: (\n message: RecordedMessage<MessageType, MessageMetadataType>,\n ) => boolean;\n processingScope?: MessageProcessingScope<HandlerContext>;\n checkpoints?: Checkpointer<\n MessageType,\n MessageMetadataType,\n HandlerContext,\n CheckpointType\n >;\n canHandle?: CanHandle<MessageType>;\n hooks?: {\n onStart?: OnReactorStartHook<HandlerContext>;\n onClose?: OnReactorCloseHook;\n };\n};\n\nexport type HandlerOptions<\n MessageType extends AnyMessage = AnyMessage,\n MessageMetadataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n HandlerContext extends DefaultRecord = DefaultRecord,\n> =\n | {\n eachMessage: SingleRecordedMessageHandlerWithContext<\n MessageType,\n MessageMetadataType,\n HandlerContext\n >;\n eachBatch?: never;\n }\n | {\n eachMessage?: never;\n eachBatch: BatchRecordedMessageHandlerWithContext<\n MessageType,\n MessageMetadataType,\n HandlerContext\n >;\n };\n\nexport type OnReactorStartHook<\n HandlerContext extends DefaultRecord = DefaultRecord,\n> = (context: HandlerContext) => Promise<void>;\n\nexport type OnReactorCloseHook = () => Promise<void>;\n\nexport type ReactorOptions<\n MessageType extends AnyMessage = AnyMessage,\n MessageMetadataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n HandlerContext extends DefaultRecord = DefaultRecord,\n CheckpointType = GlobalPositionTypeOfRecordedMessageMetadata<MessageMetadataType>,\n> = BaseMessageProcessorOptions<\n MessageType,\n MessageMetadataType,\n HandlerContext,\n CheckpointType\n> &\n HandlerOptions<MessageType, MessageMetadataType, HandlerContext>;\n\nexport type ProjectorOptions<\n EventType extends AnyEvent = AnyEvent,\n MessageMetadataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n HandlerContext extends DefaultRecord = DefaultRecord,\n CheckpointType = GlobalPositionTypeOfRecordedMessageMetadata<MessageMetadataType>,\n> = Omit<\n BaseMessageProcessorOptions<\n EventType,\n MessageMetadataType,\n HandlerContext,\n CheckpointType\n >,\n 'type' | 'processorId'\n> & { processorId?: string } & {\n truncateOnStart?: boolean;\n projection: ProjectionDefinition<\n EventType,\n MessageMetadataType,\n HandlerContext\n >;\n};\n\nexport const defaultProcessingMessageProcessingScope = <\n HandlerContext = never,\n Result = MessageHandlerResult,\n>(\n handler: (context: HandlerContext) => Result | Promise<Result>,\n partialContext: Partial<HandlerContext>,\n) => handler(partialContext as HandlerContext);\n\nexport type ReadProcessorCheckpointResult<CheckpointType = unknown> = {\n lastCheckpoint: CheckpointType | null;\n};\n\nexport type ReadProcessorCheckpoint<\n CheckpointType = unknown,\n HandlerContext extends DefaultRecord = DefaultRecord,\n> = (\n options: { processorId: string; partition?: string },\n context: HandlerContext,\n) => Promise<ReadProcessorCheckpointResult<CheckpointType>>;\n\nexport type StoreProcessorCheckpointResult<CheckpointType = unknown> =\n | {\n success: true;\n newCheckpoint: CheckpointType;\n }\n | { success: false; reason: 'IGNORED' | 'MISMATCH' };\n\nexport type StoreProcessorCheckpoint<\n MessageType extends Message = AnyMessage,\n MessageMetadataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n CheckpointType = unknown,\n HandlerContext extends DefaultRecord | undefined = undefined,\n> =\n | ((\n options: {\n message: RecordedMessage<MessageType, MessageMetadataType>;\n processorId: string;\n version: number | undefined;\n lastCheckpoint: CheckpointType | null;\n partition?: string;\n },\n context: HandlerContext,\n ) => Promise<StoreProcessorCheckpointResult<CheckpointType | null>>)\n | ((\n options: {\n message: RecordedMessage<MessageType, MessageMetadataType>;\n processorId: string;\n version: number | undefined;\n lastCheckpoint: CheckpointType | null;\n partition?: string;\n },\n context: HandlerContext,\n ) => Promise<StoreProcessorCheckpointResult<CheckpointType>>);\n\nexport const reactor = <\n MessageType extends Message = AnyMessage,\n MessageMetadataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n HandlerContext extends DefaultRecord = DefaultRecord,\n CheckpointType = GlobalPositionTypeOfRecordedMessageMetadata<MessageMetadataType>,\n>(\n options: ReactorOptions<\n MessageType,\n MessageMetadataType,\n HandlerContext,\n CheckpointType\n >,\n): MessageProcessor<\n MessageType,\n MessageMetadataType,\n HandlerContext,\n CheckpointType\n> => {\n const eachMessage: SingleMessageHandlerWithContext<\n MessageType,\n MessageMetadataType,\n HandlerContext\n > =\n 'eachMessage' in options && options.eachMessage\n ? options.eachMessage\n : () => Promise.resolve();\n let isActive = true;\n\n const { checkpoints, processorId, partition } = options;\n\n const processingScope =\n options.processingScope ?? defaultProcessingMessageProcessingScope;\n\n let lastCheckpoint: CheckpointType | null = null;\n\n return {\n id: options.processorId,\n type: options.type ?? MessageProcessorType.REACTOR,\n close: () =>\n options.hooks?.onClose ? options.hooks?.onClose() : Promise.resolve(),\n start: async (\n startOptions: Partial<HandlerContext>,\n ): Promise<CurrentMessageProcessorPosition<CheckpointType> | undefined> => {\n isActive = true;\n\n if (lastCheckpoint !== null)\n return {\n lastCheckpoint,\n };\n\n return await processingScope(async (context) => {\n if (options.hooks?.onStart) {\n await options.hooks?.onStart(context);\n }\n\n if (options.startFrom !== 'CURRENT' && options.startFrom)\n return options.startFrom;\n\n if (checkpoints) {\n const readResult = await checkpoints?.read(\n {\n processorId: processorId,\n partition: partition,\n },\n { ...startOptions, ...context },\n );\n lastCheckpoint = readResult.lastCheckpoint;\n }\n\n if (lastCheckpoint === null) return 'BEGINNING';\n\n return {\n lastCheckpoint,\n };\n }, startOptions);\n },\n get isActive() {\n return isActive;\n },\n handle: async (\n messages: RecordedMessage<MessageType, MessageMetadataType>[],\n partialContext: Partial<HandlerContext>,\n ): Promise<MessageHandlerResult> => {\n if (!isActive) return Promise.resolve();\n\n return await processingScope(async (context) => {\n let result: MessageHandlerResult = undefined;\n\n for (const message of messages) {\n if (wasMessageHandled(message, lastCheckpoint)) continue;\n\n const messageProcessingResult = await eachMessage(message, context);\n\n if (checkpoints) {\n const storeCheckpointResult: StoreProcessorCheckpointResult<CheckpointType | null> =\n await checkpoints.store(\n {\n processorId: options.processorId,\n version: options.version,\n message,\n lastCheckpoint,\n partition: options.partition,\n },\n context,\n );\n\n if (storeCheckpointResult.success) {\n // TODO: Add correct handling of the storing checkpoint\n lastCheckpoint = storeCheckpointResult.newCheckpoint;\n }\n }\n\n if (\n messageProcessingResult &&\n messageProcessingResult.type === 'STOP'\n ) {\n isActive = false;\n result = messageProcessingResult;\n break;\n }\n\n if (options.stopAfter && options.stopAfter(message)) {\n isActive = false;\n result = { type: 'STOP', reason: 'Stop condition reached' };\n break;\n }\n\n if (\n messageProcessingResult &&\n messageProcessingResult.type === 'SKIP'\n )\n continue;\n }\n\n return result;\n }, partialContext);\n },\n };\n};\n\nexport const projector = <\n EventType extends Event = Event,\n EventMetaDataType extends\n AnyRecordedMessageMetadata = AnyRecordedMessageMetadata,\n HandlerContext extends DefaultRecord = DefaultRecord,\n CheckpointType = GlobalPositionTypeOfRecordedMessageMetadata<EventMetaDataType>,\n>(\n options: ProjectorOptions<\n EventType,\n EventMetaDataType,\n HandlerContext,\n CheckpointType\n >,\n): MessageProcessor<\n EventType,\n EventMetaDataType,\n HandlerContext,\n CheckpointType\n> => {\n const { projection, ...rest } = options;\n\n return reactor<EventType, EventMetaDataType, HandlerContext, CheckpointType>({\n ...rest,\n type: MessageProcessorType.PROJECTOR,\n processorId: options.processorId ?? `projection:${projection.name}`,\n hooks: {\n onStart:\n (options.truncateOnStart && options.projection.truncate) ||\n options.hooks?.onStart\n ? async (context: HandlerContext) => {\n if (options.truncateOnStart && options.projection.truncate)\n await options.projection.truncate(context);\n\n if (options.hooks?.onStart) await options.hooks?.onStart(context);\n }\n : undefined,\n onClose: options.hooks?.onClose,\n },\n eachMessage: async (\n event: RecordedMessage<EventType, EventMetaDataType>,\n context: HandlerContext,\n ) => {\n if (!projection.canHandle.includes(event.type)) return;\n\n await projection.handle([event], context);\n },\n });\n};\n","import { EmmettError } from '../errors';\nimport { JSONParser } from '../serialization';\nimport type {\n AnyEvent,\n AnyReadEventMetadata,\n BatchRecordedMessageHandlerWithContext,\n CanHandle,\n DefaultRecord,\n Event,\n} from '../typing';\nimport { arrayUtils } from '../utils';\n\nexport type ProjectionHandlingType = 'inline' | 'async';\n\nexport type ProjectionHandler<\n EventType extends Event = AnyEvent,\n EventMetaDataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n ProjectionHandlerContext extends DefaultRecord = DefaultRecord,\n> = BatchRecordedMessageHandlerWithContext<\n EventType,\n EventMetaDataType,\n ProjectionHandlerContext\n>;\n\nexport type TruncateProjection<\n ProjectionHandlerContext extends DefaultRecord = DefaultRecord,\n> = (context: ProjectionHandlerContext) => Promise<void>;\n\nexport interface ProjectionDefinition<\n EventType extends Event = AnyEvent,\n EventMetaDataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n ProjectionHandlerContext extends DefaultRecord = DefaultRecord,\n> {\n name?: string;\n canHandle: CanHandle<EventType>;\n handle: ProjectionHandler<\n EventType,\n EventMetaDataType,\n ProjectionHandlerContext\n >;\n truncate?: TruncateProjection<ProjectionHandlerContext>;\n}\n\nexport type ProjectionRegistration<\n HandlingType extends ProjectionHandlingType,\n ReadEventMetadataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n ProjectionHandlerContext extends DefaultRecord = DefaultRecord,\n> = {\n type: HandlingType;\n projection: ProjectionDefinition<\n AnyEvent,\n ReadEventMetadataType,\n ProjectionHandlerContext\n >;\n};\n\nexport const filterProjections = <\n ReadEventMetadataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n ProjectionHandlerContext extends DefaultRecord = DefaultRecord,\n>(\n type: ProjectionHandlingType,\n projections: ProjectionRegistration<\n ProjectionHandlingType,\n ReadEventMetadataType,\n ProjectionHandlerContext\n >[],\n) => {\n const inlineProjections = projections\n .filter((projection) => projection.type === type)\n .map(({ projection }) => projection);\n\n const duplicateRegistrations = arrayUtils.getDuplicates(\n inlineProjections,\n (proj) => proj.name,\n );\n\n if (duplicateRegistrations.length > 0) {\n throw new EmmettError(`You cannot register multiple projections with the same name (or without the name).\n Ensure that:\n ${JSONParser.stringify(duplicateRegistrations)}\n have different names`);\n }\n\n return inlineProjections;\n};\n\nexport const projection = <\n EventType extends Event = Event,\n EventMetaDataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n ProjectionHandlerContext extends DefaultRecord = DefaultRecord,\n>(\n definition: ProjectionDefinition<\n EventType,\n EventMetaDataType,\n ProjectionHandlerContext\n >,\n): ProjectionDefinition<\n EventType,\n EventMetaDataType,\n ProjectionHandlerContext\n> => definition;\n\nexport const inlineProjections = <\n ReadEventMetadataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n ProjectionHandlerContext extends DefaultRecord = DefaultRecord,\n>(\n definitions: ProjectionDefinition<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any,\n ReadEventMetadataType,\n ProjectionHandlerContext\n >[],\n): ProjectionRegistration<\n 'inline',\n ReadEventMetadataType,\n ProjectionHandlerContext\n>[] =>\n definitions.map((definition) => ({\n type: 'inline',\n projection: definition,\n }));\n\nexport const asyncProjections = <\n ReadEventMetadataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n ProjectionHandlerContext extends DefaultRecord = DefaultRecord,\n>(\n definitions: ProjectionDefinition<\n AnyEvent,\n ReadEventMetadataType,\n ProjectionHandlerContext\n >[],\n): ProjectionRegistration<\n 'inline',\n ReadEventMetadataType,\n ProjectionHandlerContext\n>[] =>\n definitions.map((definition) => ({\n type: 'inline',\n projection: definition,\n }));\n\nexport const projections = {\n inline: inlineProjections,\n async: asyncProjections,\n};\n","import { filter } from './filter';\nimport { map } from './map';\nimport {\n notifyAboutNoActiveReadersStream,\n NotifyAboutNoActiveReadersStream,\n} from './notifyAboutNoActiveReaders';\nimport { reduce, ReduceTransformStream } from './reduce';\nimport { retryStream } from './retry';\nimport { skip, SkipTransformStream } from './skip';\nimport { stopAfter } from './stopAfter';\nimport { stopOn } from './stopOn';\nimport { take, TakeTransformStream } from './take';\nimport { waitAtMost } from './waitAtMost';\n\nexport const streamTransformations = {\n filter,\n take,\n TakeTransformStream,\n skip,\n SkipTransformStream,\n map,\n notifyAboutNoActiveReadersStream,\n NotifyAboutNoActiveReadersStream,\n reduce,\n ReduceTransformStream,\n retry: retryStream,\n stopAfter,\n stopOn,\n waitAtMost,\n};\n","import { type SQLExecutor } from '@event-driven-io/dumbo';\nimport type {\n BatchRecordedMessageHandlerWithoutContext,\n EmmettError,\n Message,\n ReadEventMetadataWithGlobalPosition,\n} from '@event-driven-io/emmett';\nimport { readLastMessageGlobalPosition } from '../../schema/readLastMessageGlobalPosition';\nimport {\n readMessagesBatch,\n type ReadMessagesBatchOptions,\n} from '../../schema/readMessagesBatch';\n\nexport const DefaultPostgreSQLEventStoreProcessorBatchSize = 100;\nexport const DefaultPostgreSQLEventStoreProcessorPullingFrequencyInMs = 50;\n\nexport type PostgreSQLEventStoreMessagesBatchHandlerResult = void | {\n type: 'STOP';\n reason?: string;\n error?: EmmettError;\n};\n\nexport type PostgreSQLEventStoreMessageBatchPullerOptions<\n MessageType extends Message = Message,\n> = {\n executor: SQLExecutor;\n pullingFrequencyInMs: number;\n batchSize: number;\n eachBatch: BatchRecordedMessageHandlerWithoutContext<\n MessageType,\n ReadEventMetadataWithGlobalPosition\n >;\n stopWhen?: {\n noMessagesLeft?: boolean;\n };\n};\n\nexport type PostgreSQLEventStoreMessageBatchPullerStartFrom =\n | { lastCheckpoint: bigint }\n | 'BEGINNING'\n | 'END';\n\nexport type PostgreSQLEventStoreMessageBatchPullerStartOptions = {\n startFrom: PostgreSQLEventStoreMessageBatchPullerStartFrom;\n};\n\nexport type PostgreSQLEventStoreMessageBatchPuller = {\n isRunning: boolean;\n start(\n options: PostgreSQLEventStoreMessageBatchPullerStartOptions,\n ): Promise<void>;\n stop(): Promise<void>;\n};\n\nexport const postgreSQLEventStoreMessageBatchPuller = <\n MessageType extends Message = Message,\n>({\n executor,\n batchSize,\n eachBatch,\n pullingFrequencyInMs,\n stopWhen,\n}: PostgreSQLEventStoreMessageBatchPullerOptions<MessageType>): PostgreSQLEventStoreMessageBatchPuller => {\n let isRunning = false;\n\n let start: Promise<void>;\n\n const pullMessages = async (\n options: PostgreSQLEventStoreMessageBatchPullerStartOptions,\n ) => {\n const after =\n options.startFrom === 'BEGINNING'\n ? 0n\n : options.startFrom === 'END'\n ? ((await readLastMessageGlobalPosition(executor))\n .currentGlobalPosition ?? 0n)\n : options.startFrom.lastCheckpoint;\n\n const readMessagesOptions: ReadMessagesBatchOptions = {\n after,\n batchSize,\n };\n\n let waitTime = 100;\n\n do {\n const { messages, currentGlobalPosition, areMessagesLeft } =\n await readMessagesBatch<MessageType>(executor, readMessagesOptions);\n\n if (messages.length > 0) {\n const result = await eachBatch(messages);\n\n if (result && result.type === 'STOP') {\n isRunning = false;\n break;\n }\n }\n\n readMessagesOptions.after = currentGlobalPosition;\n\n await new Promise((resolve) => setTimeout(resolve, waitTime));\n\n if (stopWhen?.noMessagesLeft === true && !areMessagesLeft) {\n isRunning = false;\n break;\n }\n\n if (!areMessagesLeft) {\n waitTime = Math.min(waitTime * 2, 1000);\n } else {\n waitTime = pullingFrequencyInMs;\n }\n } while (isRunning);\n };\n\n return {\n get isRunning() {\n return isRunning;\n },\n start: (options) => {\n if (isRunning) return start;\n\n start = (async () => {\n isRunning = true;\n\n return pullMessages(options);\n })();\n\n return start;\n },\n stop: async () => {\n if (!isRunning) return;\n isRunning = false;\n await start;\n },\n };\n};\n\nexport const zipPostgreSQLEventStoreMessageBatchPullerStartFrom = (\n options: (PostgreSQLEventStoreMessageBatchPullerStartFrom | undefined)[],\n): PostgreSQLEventStoreMessageBatchPullerStartFrom => {\n if (\n options.length === 0 ||\n options.some((o) => o === undefined || o === 'BEGINNING')\n )\n return 'BEGINNING';\n\n if (options.every((o) => o === 'END')) return 'END';\n\n return options\n .filter((o) => o !== undefined && o !== 'BEGINNING' && o !== 'END')\n .sort((a, b) => (a > b ? 1 : -1))[0]!;\n};\n","import { singleOrNull, sql, type SQLExecutor } from '@event-driven-io/dumbo';\nimport { defaultTag, messagesTable } from './typing';\n\ntype ReadLastMessageGlobalPositionSqlResult = {\n global_position: string;\n};\n\nexport type ReadLastMessageGlobalPositionResult = {\n currentGlobalPosition: bigint | null;\n};\n\nexport const readLastMessageGlobalPosition = async (\n execute: SQLExecutor,\n options?: { partition?: string },\n): Promise<ReadLastMessageGlobalPositionResult> => {\n const result = await singleOrNull(\n execute.query<ReadLastMessageGlobalPositionSqlResult>(\n sql(\n `SELECT global_position\n FROM ${messagesTable.name}\n WHERE partition = %L AND is_archived = FALSE AND transaction_id < pg_snapshot_xmin(pg_current_snapshot())\n ORDER BY transaction_id, global_position\n LIMIT 1`,\n options?.partition ?? defaultTag,\n ),\n ),\n );\n\n return {\n currentGlobalPosition:\n result !== null ? BigInt(result.global_position) : null,\n };\n};\n","export const emmettPrefix = 'emt';\n\nexport const globalTag = 'global';\nexport const defaultTag = 'emt:default';\n\nexport const globalNames = {\n module: `${emmettPrefix}:module:${globalTag}`,\n tenant: `${emmettPrefix}:tenant:${globalTag}`,\n};\n\nconst columns = {\n partition: {\n name: 'partition',\n },\n isArchived: { name: 'is_archived' },\n};\n\nexport const streamsTable = {\n name: `${emmettPrefix}_streams`,\n columns: {\n partition: columns.partition,\n isArchived: columns.isArchived,\n },\n};\n\nexport const messagesTable = {\n name: `${emmettPrefix}_messages`,\n columns: {\n partition: columns.partition,\n isArchived: columns.isArchived,\n },\n};\n\nexport const subscriptionsTable = {\n name: `${emmettPrefix}_subscriptions`,\n};\n","import { mapRows, sql, type SQLExecutor } from '@event-driven-io/dumbo';\nimport {\n type CombinedMessageMetadata,\n type Message,\n type MessageDataOf,\n type MessageMetaDataOf,\n type MessageTypeOf,\n type RecordedMessage,\n type RecordedMessageMetadata,\n type RecordedMessageMetadataWithGlobalPosition,\n} from '@event-driven-io/emmett';\nimport { defaultTag, messagesTable } from './typing';\n\ntype ReadMessagesBatchSqlResult<MessageType extends Message> = {\n stream_position: string;\n stream_id: string;\n message_data: MessageDataOf<MessageType>;\n message_metadata: MessageMetaDataOf<MessageType>;\n message_schema_version: string;\n message_type: MessageTypeOf<MessageType>;\n message_id: string;\n global_position: string;\n transaction_id: string;\n created: string;\n};\n\nexport type ReadMessagesBatchOptions =\n | {\n after: bigint;\n batchSize: number;\n }\n | {\n from: bigint;\n batchSize: number;\n }\n | { to: bigint; batchSize: number }\n | { from: bigint; to: bigint };\n\nexport type ReadMessagesBatchResult<\n MessageType extends Message,\n MessageMetadataType extends RecordedMessageMetadata = RecordedMessageMetadata,\n> = {\n currentGlobalPosition: bigint;\n messages: RecordedMessage<MessageType, MessageMetadataType>[];\n areMessagesLeft: boolean;\n};\n\nexport const readMessagesBatch = async <\n MessageType extends Message,\n RecordedMessageMetadataType extends\n RecordedMessageMetadataWithGlobalPosition = RecordedMessageMetadataWithGlobalPosition,\n>(\n execute: SQLExecutor,\n options: ReadMessagesBatchOptions & { partition?: string },\n): Promise<\n ReadMessagesBatchResult<MessageType, RecordedMessageMetadataType>\n> => {\n const from =\n 'from' in options\n ? options.from\n : 'after' in options\n ? options.after + 1n\n : 0n;\n const batchSize =\n options && 'batchSize' in options\n ? options.batchSize\n : options.to - options.from;\n\n const fromCondition: string =\n from !== -0n ? `AND global_position >= ${from}` : '';\n\n const toCondition =\n 'to' in options ? `AND global_position <= ${options.to}` : '';\n\n const limitCondition =\n 'batchSize' in options ? `LIMIT ${options.batchSize}` : '';\n\n const messages: RecordedMessage<MessageType, RecordedMessageMetadataType>[] =\n await mapRows(\n execute.query<ReadMessagesBatchSqlResult<MessageType>>(\n sql(\n `SELECT stream_id, stream_position, global_position, message_data, message_metadata, message_schema_version, message_type, message_id\n FROM ${messagesTable.name}\n WHERE partition = %L AND is_archived = FALSE AND transaction_id < pg_snapshot_xmin(pg_current_snapshot()) ${fromCondition} ${toCondition}\n ORDER BY transaction_id, global_position\n ${limitCondition}`,\n options?.partition ?? defaultTag,\n ),\n ),\n (row) => {\n const rawEvent = {\n type: row.message_type,\n data: row.message_data,\n metadata: row.message_metadata,\n } as unknown as MessageType;\n\n const metadata: RecordedMessageMetadataWithGlobalPosition = {\n ...('metadata' in rawEvent ? (rawEvent.metadata ?? {}) : {}),\n messageId: row.message_id,\n streamName: row.stream_id,\n streamPosition: BigInt(row.stream_position),\n globalPosition: BigInt(row.global_position),\n };\n\n return {\n ...rawEvent,\n kind: 'Event',\n metadata: metadata as CombinedMessageMetadata<\n MessageType,\n RecordedMessageMetadataType\n >,\n };\n },\n );\n\n return messages.length > 0\n ? {\n currentGlobalPosition:\n messages[messages.length - 1]!.metadata.globalPosition,\n messages: messages,\n areMessagesLeft: messages.length === batchSize,\n }\n : {\n currentGlobalPosition:\n 'from' in options\n ? options.from\n : 'after' in options\n ? options.after\n : 0n,\n messages: [],\n areMessagesLeft: false,\n };\n};\n","import { dumbo, type Dumbo, type SQLExecutor } from '@event-driven-io/dumbo';\nimport {\n EmmettError,\n MessageProcessor,\n type AnyEvent,\n type AnyMessage,\n type AnyRecordedMessageMetadata,\n type BatchRecordedMessageHandlerWithoutContext,\n type DefaultRecord,\n type Message,\n type MessageConsumer,\n type MessageConsumerOptions,\n type ReadEventMetadataWithGlobalPosition,\n} from '@event-driven-io/emmett';\nimport { v7 as uuid } from 'uuid';\nimport {\n DefaultPostgreSQLEventStoreProcessorBatchSize,\n DefaultPostgreSQLEventStoreProcessorPullingFrequencyInMs,\n postgreSQLEventStoreMessageBatchPuller,\n zipPostgreSQLEventStoreMessageBatchPullerStartFrom,\n type PostgreSQLEventStoreMessageBatchPuller,\n} from './messageBatchProcessing';\nimport {\n postgreSQLProjector,\n postgreSQLReactor,\n type PostgreSQLProcessor,\n type PostgreSQLProjectorOptions,\n type PostgreSQLReactorOptions,\n} from './postgreSQLProcessor';\n\nexport type PostgreSQLConsumerContext = {\n execute: SQLExecutor;\n connection: {\n connectionString: string;\n pool: Dumbo;\n };\n};\n\nexport type ExtendableContext = Partial<PostgreSQLConsumerContext> &\n DefaultRecord;\n\nexport type PostgreSQLEventStoreConsumerConfig<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ConsumerMessageType extends Message = any,\n> = MessageConsumerOptions<ConsumerMessageType> & {\n stopWhen?: {\n noMessagesLeft?: boolean;\n };\n pulling?: {\n batchSize?: number;\n pullingFrequencyInMs?: number;\n };\n};\n\nexport type PostgreSQLEventStoreConsumerOptions<\n ConsumerMessageType extends Message = Message,\n> = PostgreSQLEventStoreConsumerConfig<ConsumerMessageType> & {\n connectionString: string;\n pool?: Dumbo;\n};\n\nexport type PostgreSQLEventStoreConsumer<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ConsumerMessageType extends AnyMessage = any,\n> = MessageConsumer<ConsumerMessageType> &\n Readonly<{\n reactor: <MessageType extends AnyMessage = ConsumerMessageType>(\n options: PostgreSQLReactorOptions<MessageType>,\n ) => PostgreSQLProcessor<MessageType>;\n }> &\n (AnyEvent extends ConsumerMessageType\n ? Readonly<{\n projector: <\n EventType extends AnyEvent = ConsumerMessageType & AnyEvent,\n >(\n options: PostgreSQLProjectorOptions<EventType>,\n ) => PostgreSQLProcessor<EventType>;\n }>\n : object);\n\nexport const postgreSQLEventStoreConsumer = <\n ConsumerMessageType extends Message = AnyMessage,\n>(\n options: PostgreSQLEventStoreConsumerOptions<ConsumerMessageType>,\n): PostgreSQLEventStoreConsumer<ConsumerMessageType> => {\n let isRunning = false;\n const { pulling } = options;\n const processors = options.processors ?? [];\n\n let start: Promise<void>;\n\n let currentMessagePuller: PostgreSQLEventStoreMessageBatchPuller | undefined;\n\n const pool = options.pool\n ? options.pool\n : dumbo({ connectionString: options.connectionString });\n\n const eachBatch: BatchRecordedMessageHandlerWithoutContext<\n ConsumerMessageType,\n ReadEventMetadataWithGlobalPosition\n > = async (messagesBatch) => {\n const activeProcessors = processors.filter((s) => s.isActive);\n\n if (activeProcessors.length === 0)\n return {\n type: 'STOP',\n reason: 'No active processors',\n };\n\n const result = await Promise.allSettled(\n activeProcessors.map((s) => {\n // TODO: Add here filtering to only pass messages that can be handled by processor\n return s.handle(messagesBatch, {\n connection: {\n connectionString: options.connectionString,\n pool,\n },\n });\n }),\n );\n\n return result.some(\n (r) => r.status === 'fulfilled' && r.value?.type !== 'STOP',\n )\n ? undefined\n : {\n type: 'STOP',\n };\n };\n\n const messagePooler = (currentMessagePuller =\n postgreSQLEventStoreMessageBatchPuller({\n stopWhen: options.stopWhen,\n executor: pool.execute,\n eachBatch,\n batchSize:\n pulling?.batchSize ?? DefaultPostgreSQLEventStoreProcessorBatchSize,\n pullingFrequencyInMs:\n pulling?.pullingFrequencyInMs ??\n DefaultPostgreSQLEventStoreProcessorPullingFrequencyInMs,\n }));\n\n const stop = async () => {\n if (!isRunning) return;\n isRunning = false;\n if (currentMessagePuller) {\n await currentMessagePuller.stop();\n currentMessagePuller = undefined;\n }\n await start;\n\n await Promise.all(processors.map((p) => p.close()));\n };\n\n return {\n consumerId: options.consumerId ?? uuid(),\n get isRunning() {\n return isRunning;\n },\n processors,\n reactor: <MessageType extends AnyMessage = ConsumerMessageType>(\n options: PostgreSQLReactorOptions<MessageType>,\n ): PostgreSQLProcessor<MessageType> => {\n const processor = postgreSQLReactor(options);\n\n processors.push(\n // TODO: change that\n processor as unknown as MessageProcessor<\n ConsumerMessageType,\n AnyRecordedMessageMetadata,\n DefaultRecord\n >,\n );\n\n return processor;\n },\n projector: <EventType extends AnyEvent = ConsumerMessageType & AnyEvent>(\n options: PostgreSQLProjectorOptions<EventType>,\n ): PostgreSQLProcessor<EventType> => {\n const processor = postgreSQLProjector(options);\n\n processors.push(\n // TODO: change that\n processor as unknown as MessageProcessor<\n ConsumerMessageType,\n AnyRecordedMessageMetadata,\n DefaultRecord\n >,\n );\n\n return processor;\n },\n start: () => {\n if (isRunning) return start;\n\n start = (async () => {\n if (processors.length === 0)\n return Promise.reject(\n new EmmettError(\n 'Cannot start consumer without at least a single processor',\n ),\n );\n\n isRunning = true;\n\n const startFrom = zipPostgreSQLEventStoreMessageBatchPullerStartFrom(\n await Promise.all(\n processors.map(async (o) => {\n const result = await o.start({\n execute: pool.execute,\n connection: {\n connectionString: options.connectionString,\n pool,\n },\n });\n\n return result;\n }),\n ),\n );\n\n return messagePooler.start({ startFrom });\n })();\n\n return start;\n },\n stop,\n close: async () => {\n await stop();\n await pool.close();\n },\n };\n};\n","import {\n dumbo,\n type Dumbo,\n type NodePostgresClient,\n type NodePostgresClientConnection,\n type NodePostgresConnector,\n type NodePostgresPool,\n type NodePostgresPoolClientConnection,\n type NodePostgresTransaction,\n type SQLExecutor,\n} from '@event-driven-io/dumbo';\nimport {\n EmmettError,\n getCheckpoint,\n MessageProcessor,\n projector,\n reactor,\n type AnyEvent,\n type AnyMessage,\n type BatchRecordedMessageHandlerWithContext,\n type Checkpointer,\n type Event,\n type Message,\n type MessageHandlerResult,\n type MessageProcessingScope,\n type ProjectorOptions,\n type ReactorOptions,\n type ReadEventMetadataWithGlobalPosition,\n type SingleRecordedMessageHandlerWithContext,\n} from '@event-driven-io/emmett';\nimport pg from 'pg';\nimport { readProcessorCheckpoint, storeProcessorCheckpoint } from '../schema';\nimport type { PostgreSQLEventStoreMessageBatchPullerStartFrom } from './messageBatchProcessing';\n\nexport type PostgreSQLProcessorHandlerContext = {\n execute: SQLExecutor;\n connection: {\n connectionString: string;\n client: NodePostgresClient;\n transaction: NodePostgresTransaction;\n pool: Dumbo;\n };\n};\n\nexport type PostgreSQLProcessor<MessageType extends Message = AnyMessage> =\n MessageProcessor<\n MessageType,\n ReadEventMetadataWithGlobalPosition,\n PostgreSQLProcessorHandlerContext\n >;\n\nexport type PostgreSQLProcessorEachMessageHandler<\n MessageType extends Message = Message,\n> = SingleRecordedMessageHandlerWithContext<\n MessageType,\n ReadEventMetadataWithGlobalPosition,\n PostgreSQLProcessorHandlerContext\n>;\n\nexport type PostgreSQLProcessorEachBatchHandler<\n MessageType extends Message = Message,\n> = BatchRecordedMessageHandlerWithContext<\n MessageType,\n ReadEventMetadataWithGlobalPosition,\n PostgreSQLProcessorHandlerContext\n>;\n\nexport type PostgreSQLProcessorStartFrom =\n | PostgreSQLEventStoreMessageBatchPullerStartFrom\n | 'CURRENT';\n\ntype PostgreSQLProcessorPooledOptions =\n | {\n connector?: NodePostgresConnector;\n database?: string;\n pooled: true;\n pool: pg.Pool;\n }\n | {\n connector?: NodePostgresConnector;\n database?: string;\n pool: pg.Pool;\n }\n | {\n connector?: NodePostgresConnector;\n database?: string;\n pooled: true;\n }\n | {\n connector?: NodePostgresConnector;\n database?: string;\n };\n\ntype PostgreSQLProcessorNotPooledOptions =\n | {\n connector?: NodePostgresConnector;\n database?: string;\n pooled: false;\n client: pg.Client;\n }\n | {\n connector?: NodePostgresConnector;\n database?: string;\n client: pg.Client;\n }\n | {\n connector?: NodePostgresConnector;\n database?: string;\n pooled: false;\n }\n | {\n connector?: NodePostgresConnector;\n database?: string;\n connection:\n | NodePostgresPoolClientConnection\n | NodePostgresClientConnection;\n pooled?: false;\n }\n | {\n connector?: NodePostgresConnector;\n database?: string;\n dumbo: NodePostgresPool;\n pooled?: false;\n };\n\nexport type PostgreSQLProcessorConnectionOptions = {\n connectionString: string;\n} & (PostgreSQLProcessorPooledOptions | PostgreSQLProcessorNotPooledOptions);\n\nexport type PostgreSQLCheckpointer<\n MessageType extends AnyMessage = AnyMessage,\n> = Checkpointer<\n MessageType,\n ReadEventMetadataWithGlobalPosition,\n PostgreSQLProcessorHandlerContext\n>;\n\nexport const postgreSQLCheckpointer = <\n MessageType extends Message = Message,\n>(): PostgreSQLCheckpointer<MessageType> => ({\n read: async (options, context) => {\n const result = await readProcessorCheckpoint(context.execute, options);\n\n return { lastCheckpoint: result?.lastProcessedPosition };\n },\n store: async (options, context) => {\n const newPosition: bigint | null = getCheckpoint(options.message);\n\n const result = await storeProcessorCheckpoint(context.execute, {\n lastProcessedPosition: options.lastCheckpoint,\n newPosition,\n processorId: options.processorId,\n partition: options.partition,\n version: options.version,\n });\n\n return result.success\n ? { success: true, newCheckpoint: result.newPosition }\n : result;\n },\n});\n\ntype PostgreSQLConnectionOptions = {\n connectionOptions?: PostgreSQLProcessorConnectionOptions;\n};\n\nexport type PostgreSQLReactorOptions<MessageType extends Message = Message> =\n ReactorOptions<\n MessageType,\n ReadEventMetadataWithGlobalPosition,\n PostgreSQLProcessorHandlerContext\n > &\n PostgreSQLConnectionOptions;\n\nexport type PostgreSQLProjectorOptions<EventType extends AnyEvent = AnyEvent> =\n ProjectorOptions<\n EventType,\n ReadEventMetadataWithGlobalPosition,\n PostgreSQLProcessorHandlerContext\n > &\n PostgreSQLConnectionOptions;\n\nexport type PostgreSQLProcessorOptions<\n MessageType extends AnyMessage = AnyMessage,\n> =\n | PostgreSQLReactorOptions<MessageType>\n | PostgreSQLProjectorOptions<MessageType & AnyEvent>;\n\nconst postgreSQLProcessingScope = (options: {\n pool: Dumbo | null;\n connectionString: string | null;\n processorId: string;\n}): MessageProcessingScope<PostgreSQLProcessorHandlerContext> => {\n const processorConnectionString = options.connectionString;\n\n const processorPool = options.pool;\n\n const processingScope: MessageProcessingScope<\n PostgreSQLProcessorHandlerContext\n > = async <Result = MessageHandlerResult>(\n handler: (\n context: PostgreSQLProcessorHandlerContext,\n ) => Result | Promise<Result>,\n partialContext: Partial<PostgreSQLProcessorHandlerContext>,\n ) => {\n const connection = partialContext?.connection;\n const connectionString =\n processorConnectionString ?? connection?.connectionString;\n\n if (!connectionString)\n throw new EmmettError(\n `PostgreSQL processor '${options.processorId}' is missing connection string. Ensure that you passed it through options`,\n );\n\n const pool =\n (!processorConnectionString ||\n connectionString == processorConnectionString\n ? connection?.pool\n : processorPool) ?? processorPool;\n\n if (!pool)\n throw new EmmettError(\n `PostgreSQL processor '${options.processorId}' is missing connection string. Ensure that you passed it through options`,\n );\n\n return pool.withTransaction(async (transaction) => {\n const client =\n (await transaction.connection.open()) as NodePostgresClient;\n return handler({\n ...partialContext,\n execute: transaction.execute,\n connection: {\n connectionString,\n pool,\n client,\n transaction,\n },\n });\n });\n };\n\n return processingScope;\n};\n\nconst getProcessorPool = (options: PostgreSQLConnectionOptions) => {\n const poolOptions = {\n ...(options.connectionOptions ? options.connectionOptions : {}),\n };\n const processorConnectionString =\n 'connectionString' in poolOptions\n ? (poolOptions.connectionString ?? null)\n : null;\n\n const processorPool =\n 'dumbo' in poolOptions\n ? (poolOptions.dumbo as NodePostgresPool)\n : processorConnectionString\n ? dumbo({\n connectionString: processorConnectionString,\n ...poolOptions,\n })\n : null;\n\n return {\n pool: processorPool,\n connectionString: processorConnectionString,\n close:\n processorPool != null && !('dumbo' in poolOptions)\n ? processorPool.close\n : undefined,\n };\n};\n\nexport const postgreSQLProjector = <EventType extends Event = Event>(\n options: PostgreSQLProjectorOptions<EventType>,\n): PostgreSQLProcessor<EventType> => {\n const { pool, connectionString, close } = getProcessorPool(options);\n\n const hooks = {\n onStart: options.hooks?.onStart,\n onClose:\n options.hooks?.onClose || close\n ? async () => {\n if (options.hooks?.onClose) await options.hooks?.onClose();\n if (close) await close();\n }\n : undefined,\n };\n\n return projector<\n EventType,\n ReadEventMetadataWithGlobalPosition,\n PostgreSQLProcessorHandlerContext\n >({\n ...options,\n hooks,\n processingScope: postgreSQLProcessingScope({\n pool,\n connectionString,\n processorId:\n options.processorId ?? `projection:${options.projection.name}`,\n }),\n checkpoints: postgreSQLCheckpointer<EventType>(),\n });\n};\n\nexport const postgreSQLReactor = <MessageType extends Message = Message>(\n options: PostgreSQLReactorOptions<MessageType>,\n): PostgreSQLProcessor<MessageType> => {\n const { pool, connectionString, close } = getProcessorPool(options);\n\n const hooks = {\n onStart: options.hooks?.onStart,\n onClose:\n options.hooks?.onClose || close\n ? async () => {\n if (options.hooks?.onClose) await options.hooks?.onClose();\n if (close) await close();\n }\n : undefined,\n };\n\n return reactor({\n ...options,\n hooks,\n processingScope: postgreSQLProcessingScope({\n pool,\n connectionString,\n processorId: options.processorId,\n }),\n checkpoints: postgreSQLCheckpointer<MessageType>(),\n });\n};\n\nexport const postgreSQLMessageProcessor = <\n MessageType extends AnyMessage = AnyMessage,\n>(\n options: PostgreSQLProcessorOptions<MessageType>,\n): PostgreSQLProcessor<MessageType> => {\n if ('projection' in options) {\n return postgreSQLProjector(\n options as unknown as PostgreSQLProjectorOptions<Event>,\n ) as PostgreSQLProcessor<MessageType>;\n }\n\n return postgreSQLReactor(options);\n};\n","import { type NodePostgresPool, type SQL } from '@event-driven-io/dumbo';\nimport { appendToStreamSQL } from './appendToStream';\nimport { storeSubscriptionCheckpointSQL } from './storeProcessorCheckpoint';\nimport {\n addDefaultPartitionSQL,\n addModuleForAllTenantsSQL,\n addModuleSQL,\n addPartitionSQL,\n addTablePartitions,\n addTenantForAllModulesSQL,\n addTenantSQL,\n messagesTableSQL,\n migrationFromEventsToMessagesSQL,\n sanitizeNameSQL,\n streamsTableSQL,\n subscriptionsTableSQL,\n} from './tables';\n\nexport * from './appendToStream';\nexport * from './readLastMessageGlobalPosition';\nexport * from './readMessagesBatch';\nexport * from './readProcessorCheckpoint';\nexport * from './readStream';\nexport * from './storeProcessorCheckpoint';\nexport * from './tables';\nexport * from './typing';\n\nexport const schemaSQL: SQL[] = [\n migrationFromEventsToMessagesSQL,\n streamsTableSQL,\n messagesTableSQL,\n subscriptionsTableSQL,\n sanitizeNameSQL,\n addTablePartitions,\n addPartitionSQL,\n addModuleSQL,\n addTenantSQL,\n addModuleForAllTenantsSQL,\n addTenantForAllModulesSQL,\n appendToStreamSQL,\n addDefaultPartitionSQL,\n storeSubscriptionCheckpointSQL,\n];\n\nexport const createEventStoreSchema = async (\n pool: NodePostgresPool,\n): Promise<void> => {\n await pool.withTransaction(({ execute }) => execute.batchCommand(schemaSQL));\n};\n","import {\n rawSql,\n single,\n sql,\n type NodePostgresPool,\n type NodePostgresTransaction,\n type SQLExecutor,\n} from '@event-driven-io/dumbo';\nimport {\n JSONParser,\n NO_CONCURRENCY_CHECK,\n STREAM_DOES_NOT_EXIST,\n STREAM_EXISTS,\n type AppendToStreamOptions,\n type ExpectedStreamVersion,\n type Message,\n type RecordedMessage,\n} from '@event-driven-io/emmett';\nimport { v4 as uuid } from 'uuid';\nimport { defaultTag, messagesTable, streamsTable } from './typing';\n\nexport const appendToStreamSQL = rawSql(\n `CREATE OR REPLACE FUNCTION emt_append_to_stream(\n v_message_ids text[],\n v_messages_data jsonb[],\n v_messages_metadata jsonb[],\n v_message_schema_versions text[],\n v_message_types text[],\n v_message_kinds text[],\n v_stream_id text,\n v_stream_type text,\n v_expected_stream_position bigint DEFAULT NULL,\n v_partition text DEFAULT emt_sanitize_name('default_partition')\n ) RETURNS TABLE (\n success boolean,\n next_stream_position bigint,\n global_positions bigint[],\n transaction_id xid8\n ) LANGUAGE plpgsql\n AS $$\n DECLARE\n v_next_stream_position bigint;\n v_position bigint;\n v_updated_rows int;\n v_transaction_id xid8;\n v_global_positions bigint[];\n BEGIN\n v_transaction_id := pg_current_xact_id();\n\n IF v_expected_stream_position IS NULL THEN\n SELECT COALESCE(\n (SELECT stream_position \n FROM ${streamsTable.name}\n WHERE stream_id = v_stream_id \n AND partition = v_partition \n AND is_archived = FALSE\n LIMIT 1), \n 0\n ) INTO v_expected_stream_position;\n END IF;\n\n v_next_stream_position := v_expected_stream_position + array_upper(v_messages_data, 1);\n\n IF v_expected_stream_position = 0 THEN\n INSERT INTO ${streamsTable.name}\n (stream_id, stream_position, partition, stream_type, stream_metadata, is_archived)\n VALUES\n (v_stream_id, v_next_stream_position, v_partition, v_stream_type, '{}', FALSE);\n ELSE\n UPDATE ${streamsTable.name} as s \n SET stream_position = v_next_stream_position\n WHERE stream_id = v_stream_id AND stream_position = v_expected_stream_position AND partition = v_partition AND is_archived = FALSE;\n\n get diagnostics v_updated_rows = row_count;\n\n IF v_updated_rows = 0 THEN\n RETURN QUERY SELECT FALSE, NULL::bigint, NULL::bigint[], NULL::xid8;\n RETURN;\n END IF;\n END IF;\n\n WITH ev AS (\n SELECT row_number() OVER () + v_expected_stream_position AS stream_position, \n message_data, \n message_metadata, \n schema_version, \n message_id, \n message_type,\n message_kind\n FROM (\n SELECT *\n FROM \n unnest(v_message_ids, v_messages_data, v_messages_metadata, v_message_schema_versions, v_message_types, v_message_kinds) \n AS message(message_id, message_data, message_metadata, schema_version, message_type, message_kind)\n ) AS message\n ),\n all_messages_insert AS (\n INSERT INTO ${messagesTable.name}\n (stream_id, stream_position, partition, message_data, message_metadata, message_schema_version, message_type, message_kind, message_id, transaction_id)\n SELECT \n v_stream_id, ev.stream_position, v_partition, ev.message_data, ev.message_metadata, ev.schema_version, ev.message_type, ev.message_kind, ev.message_id, v_transaction_id\n FROM ev\n RETURNING global_position\n )\n SELECT \n array_agg(global_position ORDER BY global_position) INTO v_global_positions\n FROM \n all_messages_insert;\n\n RETURN QUERY SELECT TRUE, v_next_stream_position, v_global_positions, v_transaction_id;\n END;\n $$;\n `,\n);\n\ntype AppendToStreamResult =\n | {\n success: true;\n nextStreamPosition: bigint;\n globalPositions: bigint[];\n transactionId: string;\n }\n | { success: false };\n\nexport type AppendToStreamBeforeCommitHook = (\n messages: RecordedMessage[],\n context: {\n transaction: NodePostgresTransaction;\n },\n) => Promise<void>;\n\nexport const appendToStream = (\n pool: NodePostgresPool,\n streamName: string,\n streamType: string,\n messages: Message[],\n options?: AppendToStreamOptions & {\n partition?: string;\n beforeCommitHook?: AppendToStreamBeforeCommitHook;\n },\n): Promise<AppendToStreamResult> =>\n pool.withTransaction<AppendToStreamResult>(async (transaction) => {\n const { execute } = transaction;\n\n if (messages.length === 0)\n return { success: false, result: { success: false } };\n\n try {\n const expectedStreamVersion = toExpectedVersion(\n options?.expectedStreamVersion,\n );\n\n const messagesToAppend: RecordedMessage[] = messages.map((e) => ({\n ...e,\n kind: e.kind ?? 'Event',\n metadata: {\n messageId: uuid(),\n ...('metadata' in e ? (e.metadata ?? {}) : {}),\n },\n })) as RecordedMessage[];\n\n const {\n success,\n next_stream_position,\n global_positions,\n transaction_id,\n } = await appendEventsRaw(\n execute,\n streamName,\n streamType,\n messagesToAppend,\n {\n expectedStreamVersion,\n },\n );\n\n if (\n !success ||\n next_stream_position === null ||\n global_positions === null ||\n global_positions.length === 0 ||\n transaction_id == null\n ) {\n return {\n success: false,\n\n result: { success: false },\n };\n }\n\n const nextStreamPosition = BigInt(next_stream_position);\n\n const globalPositions = global_positions.map(BigInt);\n\n globalPositions.forEach((globalPosition, index) => {\n messagesToAppend[index]!.metadata = {\n ...messagesToAppend[index]!.metadata,\n streamName,\n streamPosition:\n nextStreamPosition -\n BigInt(messagesToAppend.length) +\n BigInt(index + 1),\n globalPosition,\n };\n });\n\n if (options?.beforeCommitHook)\n await options.beforeCommitHook(messagesToAppend, { transaction });\n\n return {\n success: true,\n\n result: {\n success: true,\n nextStreamPosition,\n globalPositions,\n transactionId: transaction_id,\n },\n };\n } catch (error) {\n if (!isOptimisticConcurrencyError(error)) throw error;\n\n return {\n success: false,\n result: { success: false },\n };\n }\n });\n\nconst toExpectedVersion = (\n expected: ExpectedStreamVersion | undefined,\n): bigint | null => {\n if (expected === undefined) return null;\n\n if (expected === NO_CONCURRENCY_CHECK) return null;\n\n // TODO: this needs to be fixed\n if (expected == STREAM_DOES_NOT_EXIST) return null;\n\n // TODO: this needs to be fixed\n if (expected == STREAM_EXISTS) return null;\n\n return expected as bigint;\n};\n\nconst isOptimisticConcurrencyError = (error: unknown): boolean =>\n error instanceof Error && 'code' in error && error.code === '23505';\n\ntype AppendToStreamSqlResult = {\n success: boolean;\n next_stream_position: string | null;\n global_positions: string[] | null;\n transaction_id: string | null | undefined;\n};\n\nconst appendEventsRaw = (\n execute: SQLExecutor,\n streamId: string,\n streamType: string,\n messages: RecordedMessage[],\n options?: {\n expectedStreamVersion: bigint | null;\n partition?: string;\n },\n): Promise<AppendToStreamSqlResult> =>\n single(\n execute.command<AppendToStreamSqlResult>(\n sql(\n `SELECT * FROM emt_append_to_stream(\n ARRAY[%s]::text[],\n ARRAY[%s]::jsonb[],\n ARRAY[%s]::jsonb[],\n ARRAY[%s]::text[],\n ARRAY[%s]::text[],\n ARRAY[%s]::text[],\n %L::text,\n %L::text,\n %s::bigint,\n %L::text\n )`,\n messages.map((e) => sql('%L', e.metadata.messageId)).join(','),\n messages.map((e) => sql('%L', JSONParser.stringify(e.data))).join(','),\n messages\n .map((e) => {\n const { messageId: _messageId, ...rawMetadata } = e.metadata;\n return sql('%L', JSONParser.stringify(rawMetadata));\n })\n .join(','),\n messages.map(() => `'1'`).join(','),\n messages.map((e) => sql('%L', e.type)).join(','),\n messages\n .map((e) => sql('%L', e.kind === 'Event' ? 'E' : 'C'))\n .join(','),\n streamId,\n streamType,\n options?.expectedStreamVersion ?? 'NULL',\n options?.partition ?? defaultTag,\n ),\n ),\n );\n","import { single, sql, type SQLExecutor } from '@event-driven-io/dumbo';\nimport { defaultTag, subscriptionsTable } from './typing';\n\nexport const storeSubscriptionCheckpointSQL = sql(`\nCREATE OR REPLACE FUNCTION store_subscription_checkpoint(\n p_subscription_id VARCHAR(100),\n p_version BIGINT,\n p_position BIGINT,\n p_check_position BIGINT,\n p_transaction_id xid8,\n p_partition TEXT DEFAULT '${defaultTag}'\n) RETURNS INT AS $$\nDECLARE\n current_position BIGINT;\nBEGIN\n -- Handle the case when p_check_position is provided\n IF p_check_position IS NOT NULL THEN\n -- Try to update if the position matches p_check_position\n UPDATE \"${subscriptionsTable.name}\"\n SET \n \"last_processed_position\" = p_position, \n \"last_processed_transaction_id\" = p_transaction_id\n WHERE \"subscription_id\" = p_subscription_id AND \"last_processed_position\" = p_check_position AND \"partition\" = p_partition;\n\n IF FOUND THEN\n RETURN 1; -- Successfully updated\n END IF;\n\n -- Retrieve the current position\n SELECT \"last_processed_position\" INTO current_position\n FROM \"${subscriptionsTable.name}\"\n WHERE \"subscription_id\" = p_subscription_id AND \"partition\" = p_partition;\n\n -- Return appropriate codes based on current position\n IF current_position = p_position THEN\n RETURN 0; -- Idempotent check: position already set\n ELSIF current_position > p_check_position THEN\n RETURN 2; -- Failure: current position is greater\n ELSE\n RETURN 2; -- Default failure case for mismatched positions\n END IF;\n END IF;\n\n -- Handle the case when p_check_position is NULL: Insert if not exists\n BEGIN\n INSERT INTO \"${subscriptionsTable.name}\"(\"subscription_id\", \"version\", \"last_processed_position\", \"partition\", \"last_processed_transaction_id\")\n VALUES (p_subscription_id, p_version, p_position, p_partition, p_transaction_id);\n RETURN 1; -- Successfully inserted\n EXCEPTION WHEN unique_violation THEN\n -- If insertion failed, it means the row already exists\n SELECT \"last_processed_position\" INTO current_position\n FROM \"${subscriptionsTable.name}\"\n WHERE \"subscription_id\" = p_subscription_id AND \"partition\" = p_partition;\n\n IF current_position = p_position THEN\n RETURN 0; -- Idempotent check: position already set\n ELSE\n RETURN 2; -- Insertion failed, row already exists with different position\n END IF;\n END;\nEND;\n$$ LANGUAGE plpgsql;\n`);\n\nexport type StoreLastProcessedProcessorPositionResult<\n Position extends bigint | null = bigint,\n> =\n | {\n success: true;\n newPosition: Position;\n }\n | { success: false; reason: 'IGNORED' | 'MISMATCH' };\n\nexport const storeProcessorCheckpoint = async <Position extends bigint | null>(\n execute: SQLExecutor,\n options: {\n processorId: string;\n version: number | undefined;\n newPosition: null extends Position ? bigint | null : bigint;\n lastProcessedPosition: bigint | null;\n partition?: string;\n },\n): Promise<\n StoreLastProcessedProcessorPositionResult<\n null extends Position ? bigint | null : bigint\n >\n> => {\n try {\n const { result } = await single(\n execute.command<{ result: 0 | 1 | 2 }>(\n sql(\n `SELECT store_subscription_checkpoint(%L, %s, %L, %L, pg_current_xact_id(), %L) as result;`,\n options.processorId,\n options.version ?? 1,\n options.newPosition,\n options.lastProcessedPosition,\n options.partition ?? defaultTag,\n ),\n ),\n );\n\n return result === 1\n ? { success: true, newPosition: options.newPosition }\n : { success: false, reason: result === 0 ? 'IGNORED' : 'MISMATCH' };\n } catch (error) {\n console.log(error);\n throw error;\n }\n};\n","import { rawSql } from '@event-driven-io/dumbo';\nimport {\n defaultTag,\n globalTag,\n messagesTable,\n streamsTable,\n subscriptionsTable,\n} from './typing';\n\nexport const streamsTableSQL = rawSql(\n `CREATE TABLE IF NOT EXISTS ${streamsTable.name}(\n stream_id TEXT NOT NULL,\n stream_position BIGINT NOT NULL,\n partition TEXT NOT NULL DEFAULT '${globalTag}',\n stream_type TEXT NOT NULL,\n stream_metadata JSONB NOT NULL,\n is_archived BOOLEAN NOT NULL DEFAULT FALSE,\n PRIMARY KEY (stream_id, partition, is_archived)\n ) PARTITION BY LIST (partition);\n \n CREATE UNIQUE INDEX IF NOT EXISTS idx_streams_unique \n ON ${streamsTable.name}(stream_id, partition, is_archived) \n INCLUDE (stream_position);`,\n);\n\nexport const messagesTableSQL = rawSql(\n `\n CREATE SEQUENCE IF NOT EXISTS emt_global_message_position;\n\n CREATE TABLE IF NOT EXISTS ${messagesTable.name}(\n stream_id TEXT NOT NULL,\n stream_position BIGINT NOT NULL,\n partition TEXT NOT NULL DEFAULT '${globalTag}',\n message_kind CHAR(1) NOT NULL DEFAULT 'E',\n message_data JSONB NOT NULL,\n message_metadata JSONB NOT NULL,\n message_schema_version TEXT NOT NULL,\n message_type TEXT NOT NULL,\n message_id TEXT NOT NULL,\n is_archived BOOLEAN NOT NULL DEFAULT FALSE,\n global_position BIGINT DEFAULT nextval('emt_global_message_position'),\n transaction_id XID8 NOT NULL,\n created TIMESTAMPTZ NOT NULL DEFAULT now(),\n PRIMARY KEY (stream_id, stream_position, partition, is_archived)\n ) PARTITION BY LIST (partition);`,\n);\n\nexport const subscriptionsTableSQL = rawSql(\n `\n CREATE TABLE IF NOT EXISTS ${subscriptionsTable.name}(\n subscription_id TEXT NOT NULL,\n version INT NOT NULL DEFAULT 1,\n partition TEXT NOT NULL DEFAULT '${globalTag}',\n last_processed_position BIGINT NOT NULL,\n last_processed_transaction_id XID8 NOT NULL,\n PRIMARY KEY (subscription_id, partition, version)\n ) PARTITION BY LIST (partition);\n`,\n);\n\nexport const sanitizeNameSQL = rawSql(\n `CREATE OR REPLACE FUNCTION emt_sanitize_name(input_name TEXT) RETURNS TEXT AS $$\n BEGIN\n RETURN REGEXP_REPLACE(input_name, '[^a-zA-Z0-9_]', '_', 'g');\n END;\n $$ LANGUAGE plpgsql;`,\n);\n\nexport const addTablePartitions = rawSql(\n `\n CREATE OR REPLACE FUNCTION emt_add_table_partition(tableName TEXT, partition_name TEXT) RETURNS void AS $$\n DECLARE\n v_main_partiton_name TEXT;\n v_active_partiton_name TEXT;\n v_archived_partiton_name TEXT;\n BEGIN \n v_main_partiton_name := emt_sanitize_name(tableName || '_' || partition_name);\n v_active_partiton_name := emt_sanitize_name(v_main_partiton_name || '_active');\n v_archived_partiton_name := emt_sanitize_name(v_main_partiton_name || '_archived');\n\n\n -- create default partition\n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I PARTITION OF %I\n FOR VALUES IN (%L) PARTITION BY LIST (is_archived);',\n v_main_partiton_name, tableName, partition_name\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I PARTITION OF %I\n FOR VALUES IN (FALSE);',\n v_active_partiton_name, v_main_partiton_name\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I PARTITION OF %I\n FOR VALUES IN (TRUE);',\n v_archived_partiton_name, v_main_partiton_name\n );\n END;\n $$ LANGUAGE plpgsql;`,\n);\n\nexport const addPartitionSQL = rawSql(\n `\n CREATE OR REPLACE FUNCTION emt_add_partition(partition_name TEXT) RETURNS void AS $$\n BEGIN \n PERFORM emt_add_table_partition('${messagesTable.name}', partition_name);\n PERFORM emt_add_table_partition('${streamsTable.name}', partition_name);\n\n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I PARTITION OF %I\n FOR VALUES IN (%L);',\n emt_sanitize_name('${subscriptionsTable.name}' || '_' || partition_name), '${subscriptionsTable.name}', partition_name\n );\n END;\n $$ LANGUAGE plpgsql;`,\n);\n\nexport const addModuleSQL = rawSql(\n `\n CREATE OR REPLACE FUNCTION add_module(new_module TEXT) RETURNS void AS $$\n BEGIN\n -- For ${messagesTable.name} table\n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I PARTITION OF %I\n FOR VALUES IN (emt_sanitize_name(%L || ''__'' || %L)) PARTITION BY LIST (is_archived);',\n emt_sanitize_name('${messagesTable.name}_' || new_module || '__' || '${globalTag}'), '${messagesTable.name}', new_module, '${globalTag}'\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I_active PARTITION OF %I\n FOR VALUES IN (FALSE);',\n emt_sanitize_name('${messagesTable.name}_' || new_module || '__' || '${globalTag}' || '_active'), emt_sanitize_name('${messagesTable.name}_' || new_module || '__' || '${globalTag}')\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I_archived PARTITION OF %I\n FOR VALUES IN (TRUE);',\n emt_sanitize_name('${messagesTable.name}_' || new_module || '__' || '${globalTag}' || '_archived'), emt_sanitize_name('${messagesTable.name}_' || new_module || '__' || '${globalTag}')\n );\n \n -- For ${streamsTable.name} table\n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I PARTITION OF %I\n FOR VALUES IN (emt_sanitize_name(%L || ''__'' || %L)) PARTITION BY LIST (is_archived);',\n emt_sanitize_name('${streamsTable.name}_' || new_module || '__' || '${globalTag}'), '${streamsTable.name}', new_module, '${globalTag}'\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I_active PARTITION OF %I\n FOR VALUES IN (FALSE);',\n emt_sanitize_name('${streamsTable.name}_' || new_module || '__' || '${globalTag}' || '_active'), emt_sanitize_name('${streamsTable.name}_' || new_module || '__' || '${globalTag}')\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I_archived PARTITION OF %I\n FOR VALUES IN (TRUE);',\n emt_sanitize_name('${streamsTable.name}_' || new_module || '__' || '${globalTag}' || '_archived'), emt_sanitize_name('${streamsTable.name}_' || new_module || '__' || '${globalTag}')\n );\n END;\n $$ LANGUAGE plpgsql;\n `,\n);\n\nexport const addTenantSQL = rawSql(\n `\n CREATE OR REPLACE FUNCTION add_tenant(new_module TEXT, new_tenant TEXT) RETURNS void AS $$\n BEGIN\n -- For ${messagesTable.name} table\n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I PARTITION OF %I\n FOR VALUES IN (emt_sanitize_name(''%s__%s'')) PARTITION BY LIST (is_archived);',\n emt_sanitize_name('${messagesTable.name}_' || new_module || '__' || new_tenant), '${messagesTable.name}', new_module, new_tenant\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I_active PARTITION OF %I\n FOR VALUES IN (FALSE);',\n emt_sanitize_name('${messagesTable.name}_' || new_module || '__' || new_tenant || '_active'), emt_sanitize_name('${messagesTable.name}_' || new_module || '__' || new_tenant)\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I_archived PARTITION OF %I\n FOR VALUES IN (TRUE);',\n emt_sanitize_name('${messagesTable.name}_' || new_module || '__' || new_tenant || '_archived'), emt_sanitize_name('${messagesTable.name}_' || new_module || '__' || new_tenant)\n );\n \n -- For ${streamsTable.name} table\n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I PARTITION OF %I\n FOR VALUES IN (emt_sanitize_name(''%s__%s'')) PARTITION BY LIST (is_archived);',\n emt_sanitize_name('${streamsTable.name}_' || new_module || '__' || new_tenant), '${streamsTable.name}', new_module, new_tenant\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I_active PARTITION OF %I\n FOR VALUES IN (FALSE);',\n emt_sanitize_name('${streamsTable.name}_' || new_module || '__' || new_tenant || '_active'), emt_sanitize_name('${streamsTable.name}_' || new_module || '__' || new_tenant)\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I_archived PARTITION OF %I\n FOR VALUES IN (TRUE);',\n emt_sanitize_name('${streamsTable.name}_' || new_module || '__' || new_tenant || '_archived'), emt_sanitize_name('${streamsTable.name}_' || new_module || '__' || new_tenant)\n );\n END;\n $$ LANGUAGE plpgsql;\n `,\n);\n\nexport const addModuleForAllTenantsSQL = rawSql(\n `\n CREATE OR REPLACE FUNCTION add_module_for_all_tenants(new_module TEXT) RETURNS void AS $$\n DECLARE\n tenant_record RECORD;\n BEGIN\n PERFORM add_module(new_module);\n \n FOR tenant_record IN SELECT DISTINCT tenant FROM ${messagesTable.name}\n LOOP\n -- For ${messagesTable.name} table\n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I PARTITION OF %I\n FOR VALUES IN (emt_sanitize_name(''%s__%s'')) PARTITION BY LIST (is_archived);',\n emt_sanitize_name('${messagesTable.name}_' || new_module || '__' || tenant_record.tenant), '${messagesTable.name}', new_module, tenant_record.tenant\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I_active PARTITION OF %I\n FOR VALUES IN (FALSE);',\n emt_sanitize_name('${messagesTable.name}_' || new_module || '__' || tenant_record.tenant || '_active'), emt_sanitize_name('${messagesTable.name}_' || new_module || '__' || tenant_record.tenant)\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I_archived PARTITION OF %I\n FOR VALUES IN (TRUE);',\n emt_sanitize_name('${messagesTable.name}_' || new_module || '__' || tenant_record.tenant || '_archived'), emt_sanitize_name('${messagesTable.name}_' || new_module || '__' || tenant_record.tenant)\n );\n \n -- For ${streamsTable.name} table\n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I PARTITION OF %I\n FOR VALUES IN (emt_sanitize_name(''%s__%s'')) PARTITION BY LIST (is_archived);',\n emt_sanitize_name('${streamsTable.name}_' || new_module || '__' || tenant_record.tenant), '${streamsTable.name}', new_module, tenant_record.tenant\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I_active PARTITION OF %I\n FOR VALUES IN (FALSE);',\n emt_sanitize_name('${streamsTable.name}_' || new_module || '__' || tenant_record.tenant || '_active'), emt_sanitize_name('${streamsTable.name}_' || new_module || '__' || tenant_record.tenant)\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I_archived PARTITION OF %I\n FOR VALUES IN (TRUE);',\n emt_sanitize_name('${streamsTable.name}_' || new_module || '__' || tenant_record.tenant || '_archived'), emt_sanitize_name('${streamsTable.name}_' || new_module || '__' || tenant_record.tenant)\n );\n END LOOP;\n END;\n $$ LANGUAGE plpgsql;\n `,\n);\n\nexport const addTenantForAllModulesSQL = rawSql(\n `\n CREATE OR REPLACE FUNCTION add_tenant_for_all_modules(new_tenant TEXT) RETURNS void AS $$\n DECLARE\n module_record RECORD;\n BEGIN\n FOR module_record IN SELECT DISTINCT partitionname FROM pg_partman.part_config WHERE parent_table = '${messagesTable.name}'\n LOOP\n -- For ${messagesTable.name} table\n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I PARTITION OF %I\n FOR VALUES IN (emt_sanitize_name(''%s__%s'')) PARTITION BY LIST (is_archived);',\n emt_sanitize_name('${messagesTable.name}_' || module_record.partitionname || '__' || new_tenant), '${messagesTable.name}', module_record.partitionname, new_tenant\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I_active PARTITION OF %I\n FOR VALUES IN (FALSE);',\n emt_sanitize_name('${messagesTable.name}_' || module_record.partitionname || '__' || new_tenant || '_active'), emt_sanitize_name('${messagesTable.name}_' || module_record.partitionname || '__' || new_tenant)\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I_archived PARTITION OF %I\n FOR VALUES IN (TRUE);',\n emt_sanitize_name('${messagesTable.name}_' || module_record.partitionname || '__' || new_tenant || '_archived'), emt_sanitize_name('${messagesTable.name}_' || module_record.partitionname || '__' || new_tenant)\n );\n \n -- For ${streamsTable.name} table\n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I PARTITION OF %I\n FOR VALUES IN (emt_sanitize_name(''%s__%s'')) PARTITION BY LIST (is_archived);',\n emt_sanitize_name('${streamsTable.name}_' || module_record.partitionname || '__' || new_tenant), '${streamsTable.name}', module_record.partitionname, new_tenant\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I_active PARTITION OF %I\n FOR VALUES IN (FALSE);',\n emt_sanitize_name('${streamsTable.name}_' || module_record.partitionname || '__' || new_tenant || '_active'), emt_sanitize_name('${streamsTable.name}_' || module_record.partitionname || '__' || new_tenant)\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I_archived PARTITION OF %I\n FOR VALUES IN (TRUE);',\n emt_sanitize_name('${streamsTable.name}_' || module_record.partitionname || '__' || new_tenant || '_archived'), emt_sanitize_name('${streamsTable.name}_' || module_record.partitionname || '__' || new_tenant)\n );\n END LOOP;\n END;\n $$ LANGUAGE plpgsql;\n `,\n);\n\nexport const addDefaultPartitionSQL = rawSql(\n `SELECT emt_add_partition('${defaultTag}');`,\n);\n\nexport const migrationFromEventsToMessagesSQL = rawSql(`\nDO $$ \nDECLARE\n partition_record RECORD;\nBEGIN\n -- Rename the main table and its columns if it exists\n IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'emt_events') THEN\n -- Rename all partitions first\n FOR partition_record IN \n SELECT tablename \n FROM pg_tables \n WHERE tablename LIKE 'emt_events_%'\n ORDER BY tablename DESC -- to handle child partitions first\n LOOP\n EXECUTE format('ALTER TABLE %I RENAME TO %I', \n partition_record.tablename, \n REPLACE(partition_record.tablename, 'events', 'messages'));\n END LOOP;\n\n -- Rename the main table\n ALTER TABLE emt_events RENAME TO emt_messages;\n \n -- Rename columns\n ALTER TABLE emt_messages \n RENAME COLUMN event_data TO message_data;\n ALTER TABLE emt_messages \n RENAME COLUMN event_metadata TO message_metadata;\n ALTER TABLE emt_messages \n RENAME COLUMN event_schema_version TO message_schema_version;\n ALTER TABLE emt_messages \n RENAME COLUMN event_type TO message_type;\n ALTER TABLE emt_messages \n RENAME COLUMN event_id TO message_id;\n ALTER TABLE emt_messages \n ADD COLUMN message_kind CHAR(1) NOT NULL DEFAULT 'E';\n\n -- Rename sequence if it exists\n IF EXISTS (SELECT 1 FROM pg_sequences WHERE sequencename = 'emt_global_event_position') THEN\n ALTER SEQUENCE emt_global_event_position \n RENAME TO emt_global_message_position;\n \n ALTER TABLE emt_messages \n ALTER COLUMN global_position \n SET DEFAULT nextval('emt_global_message_position');\n END IF;\n END IF;\nEND $$;`);\n","import { singleOrNull, sql, type SQLExecutor } from '@event-driven-io/dumbo';\nimport { defaultTag, subscriptionsTable } from './typing';\n\ntype ReadProcessorCheckpointSqlResult = {\n last_processed_position: string;\n};\n\nexport type ReadProcessorCheckpointResult = {\n lastProcessedPosition: bigint | null;\n};\n\nexport const readProcessorCheckpoint = async (\n execute: SQLExecutor,\n options: { processorId: string; partition?: string },\n): Promise<ReadProcessorCheckpointResult> => {\n const result = await singleOrNull(\n execute.query<ReadProcessorCheckpointSqlResult>(\n sql(\n `SELECT last_processed_position\n FROM ${subscriptionsTable.name}\n WHERE partition = %L AND subscription_id = %L\n LIMIT 1`,\n options?.partition ?? defaultTag,\n options.processorId,\n ),\n ),\n );\n\n return {\n lastProcessedPosition:\n result !== null ? BigInt(result.last_processed_position) : null,\n };\n};\n","import { mapRows, sql, type SQLExecutor } from '@event-driven-io/dumbo';\nimport {\n type CombinedReadEventMetadata,\n type Event,\n type EventDataOf,\n type EventMetaDataOf,\n type EventTypeOf,\n type ReadEvent,\n type ReadEventMetadataWithGlobalPosition,\n type ReadStreamOptions,\n type ReadStreamResult,\n} from '@event-driven-io/emmett';\nimport { PostgreSQLEventStoreDefaultStreamVersion } from '../postgreSQLEventStore';\nimport { defaultTag, messagesTable } from './typing';\n\ntype ReadStreamSqlResult<EventType extends Event> = {\n stream_position: string;\n message_data: EventDataOf<EventType>;\n message_metadata: EventMetaDataOf<EventType>;\n message_schema_version: string;\n message_type: EventTypeOf<EventType>;\n message_id: string;\n global_position: string;\n transaction_id: string;\n created: string;\n};\n\nexport const readStream = async <EventType extends Event>(\n execute: SQLExecutor,\n streamId: string,\n options?: ReadStreamOptions & { partition?: string },\n): Promise<\n ReadStreamResult<EventType, ReadEventMetadataWithGlobalPosition>\n> => {\n const fromCondition: string =\n options && 'from' in options\n ? `AND stream_position >= ${options.from}`\n : '';\n\n const to = Number(\n options && 'to' in options\n ? options.to\n : options && 'maxCount' in options && options.maxCount\n ? options.from + options.maxCount\n : NaN,\n );\n\n const toCondition = !isNaN(to) ? `AND stream_position <= ${to}` : '';\n\n const events: ReadEvent<EventType, ReadEventMetadataWithGlobalPosition>[] =\n await mapRows(\n execute.query<ReadStreamSqlResult<EventType>>(\n sql(\n `SELECT stream_id, stream_position, global_position, message_data, message_metadata, message_schema_version, message_type, message_id\n FROM ${messagesTable.name}\n WHERE stream_id = %L AND partition = %L AND is_archived = FALSE ${fromCondition} ${toCondition}\n ORDER BY stream_position ASC`,\n streamId,\n options?.partition ?? defaultTag,\n ),\n ),\n (row) => {\n const rawEvent = {\n type: row.message_type,\n data: row.message_data,\n metadata: row.message_metadata,\n } as unknown as EventType;\n\n const metadata: ReadEventMetadataWithGlobalPosition = {\n ...('metadata' in rawEvent ? (rawEvent.metadata ?? {}) : {}),\n messageId: row.message_id,\n streamName: streamId,\n streamPosition: BigInt(row.stream_position),\n globalPosition: BigInt(row.global_position),\n };\n\n return {\n ...rawEvent,\n kind: 'Event',\n metadata: metadata as CombinedReadEventMetadata<\n EventType,\n ReadEventMetadataWithGlobalPosition\n >,\n };\n },\n );\n\n return events.length > 0\n ? {\n currentStreamVersion:\n events[events.length - 1]!.metadata.streamPosition,\n events,\n streamExists: true,\n }\n : {\n currentStreamVersion: PostgreSQLEventStoreDefaultStreamVersion,\n events: [],\n streamExists: false,\n };\n};\n","import type {\n AnyEvent,\n ProjectorOptions,\n ReadEventMetadataWithGlobalPosition,\n} from '@event-driven-io/emmett';\nimport { v7 as uuid } from 'uuid';\nimport type { PostgreSQLProjectionDefinition } from '../projections';\nimport {\n postgreSQLEventStoreConsumer,\n type PostgreSQLEventStoreConsumer,\n type PostgreSQLEventStoreConsumerOptions,\n} from './postgreSQLEventStoreConsumer';\nimport type { PostgreSQLProcessorHandlerContext } from './postgreSQLProcessor';\n\nexport const rebuildPostgreSQLProjections = <\n EventType extends AnyEvent = AnyEvent,\n>(\n options: Omit<\n PostgreSQLEventStoreConsumerOptions<EventType>,\n 'stopWhen' | 'processors'\n > &\n (\n | {\n projections: (\n | ProjectorOptions<\n EventType,\n ReadEventMetadataWithGlobalPosition,\n PostgreSQLProcessorHandlerContext\n >\n | PostgreSQLProjectionDefinition<EventType>\n )[];\n }\n | ProjectorOptions<\n EventType,\n ReadEventMetadataWithGlobalPosition,\n PostgreSQLProcessorHandlerContext\n >\n ),\n): PostgreSQLEventStoreConsumer<EventType> => {\n const consumer = postgreSQLEventStoreConsumer({\n ...options,\n stopWhen: { noMessagesLeft: true },\n });\n\n const projections: (Omit<\n ProjectorOptions<\n EventType,\n ReadEventMetadataWithGlobalPosition,\n PostgreSQLProcessorHandlerContext\n >,\n 'processorId'\n > & { processorId?: string })[] =\n 'projections' in options\n ? options.projections.map((p) =>\n 'projection' in p\n ? {\n ...p,\n processorId: `projection:${p.projection.name ?? uuid()}-rebuild`,\n truncateOnStart: p.truncateOnStart ?? true,\n }\n : {\n projection: p,\n processorId: `projection:${p.name ?? uuid()}-rebuild`,\n truncateOnStart: true,\n },\n )\n : [options];\n\n for (const projectionDefinition of projections) {\n consumer.projector({\n ...projectionDefinition,\n processorId:\n projectionDefinition.processorId ??\n `projection:${projectionDefinition.projection.name ?? uuid()}-rebuild`,\n truncateOnStart: projectionDefinition.truncateOnStart ?? true,\n });\n }\n\n return consumer;\n};\n","import {\n type CanHandle,\n type Event,\n type ReadEvent,\n type TruncateProjection,\n} from '@event-driven-io/emmett';\nimport {\n pongoClient,\n type PongoClient,\n type PongoDocument,\n} from '@event-driven-io/pongo';\nimport {\n postgreSQLProjection,\n type PostgreSQLProjectionDefinition,\n type PostgreSQLProjectionHandlerContext,\n} from '..';\nimport type { PostgresReadEventMetadata } from '../../postgreSQLEventStore';\n\nexport type PongoProjectionHandlerContext =\n PostgreSQLProjectionHandlerContext & {\n pongo: PongoClient;\n };\n\nexport type PongoWithNotNullDocumentEvolve<\n Document extends PongoDocument,\n EventType extends Event,\n EventMetaDataType extends\n PostgresReadEventMetadata = PostgresReadEventMetadata,\n> =\n | ((\n document: Document,\n event: ReadEvent<EventType, EventMetaDataType>,\n ) => Document | null)\n | ((\n document: Document,\n event: ReadEvent<EventType>,\n ) => Promise<Document | null>);\n\nexport type PongoWithNullableDocumentEvolve<\n Document extends PongoDocument,\n EventType extends Event,\n EventMetaDataType extends\n PostgresReadEventMetadata = PostgresReadEventMetadata,\n> =\n | ((\n document: Document | null,\n event: ReadEvent<EventType, EventMetaDataType>,\n ) => Document | null)\n | ((\n document: Document | null,\n event: ReadEvent<EventType>,\n ) => Promise<Document | null>);\n\nexport type PongoDocumentEvolve<\n Document extends PongoDocument,\n EventType extends Event,\n EventMetaDataType extends\n PostgresReadEventMetadata = PostgresReadEventMetadata,\n> =\n | PongoWithNotNullDocumentEvolve<Document, EventType, EventMetaDataType>\n | PongoWithNullableDocumentEvolve<Document, EventType, EventMetaDataType>;\n\nexport type PongoProjectionOptions<EventType extends Event> = {\n handle: (\n events: ReadEvent<EventType, PostgresReadEventMetadata>[],\n context: PongoProjectionHandlerContext,\n ) => Promise<void>;\n canHandle: CanHandle<EventType>;\n truncate?: TruncateProjection<PongoProjectionHandlerContext>;\n};\n\nexport const pongoProjection = <EventType extends Event>({\n truncate,\n handle,\n canHandle,\n}: PongoProjectionOptions<EventType>): PostgreSQLProjectionDefinition<EventType> =>\n postgreSQLProjection<EventType>({\n canHandle,\n handle: async (events, context) => {\n const {\n connection: { connectionString, client, pool },\n } = context;\n const pongo = pongoClient(connectionString, {\n connectionOptions: { client, pool },\n });\n await handle(events, {\n ...context,\n pongo,\n });\n },\n truncate: truncate\n ? (context) => {\n const {\n connection: { connectionString, client, pool },\n } = context;\n const pongo = pongoClient(connectionString, {\n connectionOptions: { client, pool },\n });\n return truncate({\n ...context,\n pongo,\n });\n }\n : undefined,\n });\n\nexport type PongoMultiStreamProjectionOptions<\n Document extends PongoDocument,\n EventType extends Event,\n EventMetaDataType extends\n PostgresReadEventMetadata = PostgresReadEventMetadata,\n> = {\n canHandle: CanHandle<EventType>;\n\n collectionName: string;\n getDocumentId: (event: ReadEvent<EventType>) => string;\n} & (\n | {\n evolve: PongoWithNullableDocumentEvolve<\n Document,\n EventType,\n EventMetaDataType\n >;\n }\n | {\n evolve: PongoWithNotNullDocumentEvolve<\n Document,\n EventType,\n EventMetaDataType\n >;\n initialState: () => Document;\n }\n);\n\nexport const pongoMultiStreamProjection = <\n Document extends PongoDocument,\n EventType extends Event,\n EventMetaDataType extends\n PostgresReadEventMetadata = PostgresReadEventMetadata,\n>(\n options: PongoMultiStreamProjectionOptions<\n Document,\n EventType,\n EventMetaDataType\n >,\n): PostgreSQLProjectionDefinition<EventType> => {\n const { collectionName, getDocumentId, canHandle } = options;\n\n return pongoProjection({\n handle: async (events, { pongo }) => {\n const collection = pongo.db().collection<Document>(collectionName);\n\n for (const event of events) {\n await collection.handle(getDocumentId(event), async (document) => {\n return 'initialState' in options\n ? await options.evolve(\n document ?? options.initialState(),\n event as ReadEvent<EventType, EventMetaDataType>,\n )\n : await options.evolve(\n document,\n event as ReadEvent<EventType, EventMetaDataType>,\n );\n });\n }\n },\n canHandle,\n truncate: async (context) => {\n const {\n connection: { connectionString, client, pool },\n } = context;\n const pongo = pongoClient(connectionString, {\n connectionOptions: { client, pool },\n });\n\n await pongo.db().collection<Document>(collectionName).deleteMany();\n },\n });\n};\n\nexport type PongoSingleStreamProjectionOptions<\n Document extends PongoDocument,\n EventType extends Event,\n EventMetaDataType extends\n PostgresReadEventMetadata = PostgresReadEventMetadata,\n> = {\n canHandle: CanHandle<EventType>;\n getDocumentId?: (event: ReadEvent<EventType>) => string;\n\n collectionName: string;\n} & (\n | {\n evolve: PongoWithNullableDocumentEvolve<\n Document,\n EventType,\n EventMetaDataType\n >;\n }\n | {\n evolve: PongoWithNotNullDocumentEvolve<\n Document,\n EventType,\n EventMetaDataType\n >;\n initialState: () => Document;\n }\n);\n\nexport const pongoSingleStreamProjection = <\n Document extends PongoDocument,\n EventType extends Event,\n EventMetaDataType extends\n PostgresReadEventMetadata = PostgresReadEventMetadata,\n>(\n options: PongoSingleStreamProjectionOptions<\n Document,\n EventType,\n EventMetaDataType\n >,\n): PostgreSQLProjectionDefinition<EventType> => {\n return pongoMultiStreamProjection<Document, EventType, EventMetaDataType>({\n ...options,\n getDocumentId:\n options.getDocumentId ?? ((event) => event.metadata.streamName),\n });\n};\n","import { type Dumbo } from '@event-driven-io/dumbo';\nimport {\n assertDeepEqual,\n assertEqual,\n assertIsNotNull,\n assertThatArray,\n} from '@event-driven-io/emmett';\nimport {\n pongoClient,\n type PongoCollection,\n type PongoDocument,\n type PongoFilter,\n type WithId,\n} from '@event-driven-io/pongo';\nimport { type PostgreSQLProjectionAssert } from '..';\n\nexport type PongoAssertOptions = {\n inCollection: string;\n inDatabase?: string;\n};\n\nconst withCollection = (\n handle: (collection: PongoCollection<PongoDocument>) => Promise<void>,\n options: {\n pool: Dumbo;\n connectionString: string;\n } & PongoAssertOptions,\n) => {\n const { pool, connectionString, inDatabase, inCollection } = options;\n\n return pool.withConnection(async (connection) => {\n const pongo = pongoClient(connectionString, {\n connectionOptions: { connection },\n });\n try {\n const collection = pongo.db(inDatabase).collection(inCollection);\n\n return handle(collection);\n } finally {\n await pongo.close();\n }\n });\n};\n\nconst withoutIdAndVersion = <Doc extends PongoDocument | WithId<PongoDocument>>(\n doc: Doc,\n) => {\n const { _id, _version, ...without } = doc;\n\n return without;\n};\n\nconst assertDocumentsEqual = <\n Doc extends PongoDocument | WithId<PongoDocument>,\n>(\n actual: PongoDocument,\n expected: Doc,\n) => {\n if ('_id' in expected)\n assertEqual(\n expected._id,\n actual._id,\n // eslint-disable-next-line @typescript-eslint/restrict-template-expressions\n `Document ids are not matching! Expected: ${expected._id}, Actual: ${actual._id}`,\n );\n\n return assertDeepEqual(\n withoutIdAndVersion(actual),\n withoutIdAndVersion(expected),\n );\n};\n\ntype FilterOrId<Doc extends PongoDocument | WithId<PongoDocument>> =\n | { withId: string }\n | {\n matchingFilter: PongoFilter<Doc>;\n };\n\nexport const documentExists =\n <Doc extends PongoDocument | WithId<PongoDocument>>(\n document: Doc,\n options: PongoAssertOptions & FilterOrId<Doc>,\n ): PostgreSQLProjectionAssert =>\n (assertOptions) =>\n withCollection(\n async (collection) => {\n const result = await collection.findOne(\n 'withId' in options\n ? { _id: options.withId }\n : options.matchingFilter,\n );\n\n assertIsNotNull(result);\n\n assertDocumentsEqual(result, document);\n },\n { ...options, ...assertOptions },\n );\n\nexport const documentsAreTheSame =\n <Doc extends PongoDocument | WithId<PongoDocument>>(\n documents: Doc[],\n options: PongoAssertOptions & FilterOrId<Doc>,\n ): PostgreSQLProjectionAssert =>\n (assertOptions) =>\n withCollection(\n async (collection) => {\n const result = await collection.find(\n 'withId' in options\n ? { _id: options.withId }\n : options.matchingFilter,\n );\n\n assertEqual(\n documents.length,\n result.length,\n 'Different Documents Count than expected',\n );\n\n for (let i = 0; i < documents.length; i++) {\n assertThatArray(result as Doc[]).contains(documents[i]!);\n }\n },\n { ...options, ...assertOptions },\n );\n\nexport const documentsMatchingHaveCount =\n <Doc extends PongoDocument | WithId<PongoDocument>>(\n expectedCount: number,\n options: PongoAssertOptions & FilterOrId<Doc>,\n ): PostgreSQLProjectionAssert =>\n (assertOptions) =>\n withCollection(\n async (collection) => {\n const result = await collection.find(\n 'withId' in options\n ? { _id: options.withId }\n : options.matchingFilter,\n );\n\n assertEqual(\n expectedCount,\n result.length,\n 'Different Documents Count than expected',\n );\n },\n { ...options, ...assertOptions },\n );\n\nexport const documentMatchingExists =\n <Doc extends PongoDocument | WithId<PongoDocument>>(\n options: PongoAssertOptions & FilterOrId<Doc>,\n ): PostgreSQLProjectionAssert =>\n (assertOptions) =>\n withCollection(\n async (collection) => {\n const result = await collection.find(\n 'withId' in options\n ? { _id: options.withId }\n : options.matchingFilter,\n );\n\n assertThatArray(result).isNotEmpty();\n },\n { ...options, ...assertOptions },\n );\n\nexport const documentDoesNotExist =\n <Doc extends PongoDocument | WithId<PongoDocument>>(\n options: PongoAssertOptions & FilterOrId<Doc>,\n ): PostgreSQLProjectionAssert =>\n (assertOptions) =>\n withCollection(\n async (collection) => {\n const result = await collection.findOne(\n 'withId' in options\n ? { _id: options.withId }\n : options.matchingFilter,\n );\n\n assertIsNotNull(result);\n },\n { ...options, ...assertOptions },\n );\n\nexport const expectPongoDocuments = {\n fromCollection: <Doc extends PongoDocument | WithId<PongoDocument>>(\n collectionName: string,\n ) => {\n return {\n withId: (id: string) => {\n return {\n toBeEqual: (document: Doc) =>\n documentExists(document, {\n withId: id,\n inCollection: collectionName,\n }),\n toExist: () =>\n documentMatchingExists({\n withId: id,\n inCollection: collectionName,\n }),\n notToExist: () =>\n documentDoesNotExist({\n withId: id,\n inCollection: collectionName,\n }),\n };\n },\n matching: <Doc extends PongoDocument | WithId<PongoDocument>>(\n filter: PongoFilter<Doc>,\n ) => {\n return {\n toBeTheSame: (documents: Doc[]) =>\n documentsAreTheSame<Doc>(documents, {\n matchingFilter: filter,\n inCollection: collectionName,\n }),\n toHaveCount: (expectedCount: number) =>\n documentsMatchingHaveCount(expectedCount, {\n matchingFilter: filter,\n inCollection: collectionName,\n }),\n toExist: () =>\n documentMatchingExists({\n matchingFilter: filter,\n inCollection: collectionName,\n }),\n notToExist: () =>\n documentDoesNotExist({\n matchingFilter: filter,\n inCollection: collectionName,\n }),\n };\n },\n };\n },\n};\n","import {\n dumbo,\n type Dumbo,\n type DumboOptions,\n type QueryResultRow,\n type SQL,\n} from '@event-driven-io/dumbo';\nimport {\n assertFails,\n AssertionError,\n assertThatArray,\n assertTrue,\n isErrorConstructor,\n type CombinedReadEventMetadata,\n type Event,\n type ReadEvent,\n type ThenThrows,\n} from '@event-driven-io/emmett';\nimport { v4 as uuid } from 'uuid';\nimport { handleProjections, type PostgreSQLProjectionDefinition } from '.';\nimport type { PostgresReadEventMetadata } from '../postgreSQLEventStore';\n\nexport type PostgreSQLProjectionSpecEvent<\n EventType extends Event,\n EventMetaDataType extends\n PostgresReadEventMetadata = PostgresReadEventMetadata,\n> = EventType & {\n metadata?: Partial<EventMetaDataType>;\n};\n\nexport type PostgreSQLProjectionSpecWhenOptions = { numberOfTimes: number };\n\nexport type PostgreSQLProjectionSpec<EventType extends Event> = (\n givenEvents: PostgreSQLProjectionSpecEvent<EventType>[],\n) => {\n when: (\n events: PostgreSQLProjectionSpecEvent<EventType>[],\n options?: PostgreSQLProjectionSpecWhenOptions,\n ) => {\n then: (\n assert: PostgreSQLProjectionAssert,\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 PostgreSQLProjectionAssert = (options: {\n pool: Dumbo;\n connectionString: string;\n}) => Promise<void | boolean>;\n\nexport type PostgreSQLProjectionSpecOptions<EventType extends Event> = {\n projection: PostgreSQLProjectionDefinition<EventType>;\n} & DumboOptions;\n\nexport const PostgreSQLProjectionSpec = {\n for: <EventType extends Event>(\n options: PostgreSQLProjectionSpecOptions<EventType>,\n ): PostgreSQLProjectionSpec<EventType> => {\n {\n const { projection, ...dumoOptions } = options;\n const { connectionString } = dumoOptions;\n\n return (givenEvents: PostgreSQLProjectionSpecEvent<EventType>[]) => {\n return {\n when: (\n events: PostgreSQLProjectionSpecEvent<EventType>[],\n options?: PostgreSQLProjectionSpecWhenOptions,\n ) => {\n const allEvents: ReadEvent<EventType, PostgresReadEventMetadata>[] =\n [];\n\n const run = async (pool: Dumbo) => {\n let globalPosition = 0n;\n const numberOfTimes = options?.numberOfTimes ?? 1;\n\n for (const event of [\n ...givenEvents,\n ...Array.from({ length: numberOfTimes }).flatMap(() => events),\n ]) {\n const metadata: PostgresReadEventMetadata = {\n globalPosition: ++globalPosition,\n streamPosition: globalPosition,\n streamName: `test-${uuid()}`,\n messageId: uuid(),\n };\n\n allEvents.push({\n ...event,\n kind: 'Event',\n metadata: {\n ...metadata,\n ...('metadata' in event ? (event.metadata ?? {}) : {}),\n } as CombinedReadEventMetadata<\n EventType,\n PostgresReadEventMetadata\n >,\n });\n }\n\n await pool.withTransaction((transaction) =>\n handleProjections({\n events: allEvents,\n projections: [projection],\n connection: {\n pool,\n connectionString,\n transaction,\n },\n }),\n );\n };\n\n return {\n then: async (\n assert: PostgreSQLProjectionAssert,\n message?: string,\n ): Promise<void> => {\n const pool = dumbo(dumoOptions);\n try {\n await run(pool);\n\n const succeeded = await assert({ pool, connectionString });\n\n if (succeeded !== undefined && succeeded === false)\n assertFails(\n message ??\n \"Projection specification didn't match the criteria\",\n );\n } finally {\n await pool.close();\n }\n },\n thenThrows: async <ErrorType extends Error>(\n ...args: Parameters<ThenThrows<ErrorType>>\n ): Promise<void> => {\n const pool = dumbo(dumoOptions);\n try {\n await run(pool);\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 pool.close();\n }\n },\n };\n },\n };\n };\n }\n },\n};\n\nexport const eventInStream = <\n EventType extends Event = Event,\n EventMetaDataType extends\n PostgresReadEventMetadata = PostgresReadEventMetadata,\n>(\n streamName: string,\n event: PostgreSQLProjectionSpecEvent<EventType, EventMetaDataType>,\n): PostgreSQLProjectionSpecEvent<EventType, EventMetaDataType> => {\n return {\n ...event,\n metadata: {\n ...(event.metadata ?? {}),\n streamName: event.metadata?.streamName ?? streamName,\n } as Partial<EventMetaDataType>,\n };\n};\n\nexport const eventsInStream = <\n EventType extends Event = Event,\n EventMetaDataType extends\n PostgresReadEventMetadata = PostgresReadEventMetadata,\n>(\n streamName: string,\n events: PostgreSQLProjectionSpecEvent<EventType, EventMetaDataType>[],\n): PostgreSQLProjectionSpecEvent<EventType, EventMetaDataType>[] => {\n return events.map((e) => eventInStream(streamName, e));\n};\n\nexport const newEventsInStream = eventsInStream;\n\nexport const assertSQLQueryResultMatches =\n <T extends QueryResultRow>(sql: SQL, rows: T[]): PostgreSQLProjectionAssert =>\n async ({ pool: { execute } }) => {\n const result = await execute.query<T>(sql);\n\n assertThatArray(rows).containsExactlyInAnyOrder(result.rows);\n };\n\nexport const expectSQL = {\n query: (sql: SQL) => ({\n resultRows: {\n toBeTheSame: <T extends QueryResultRow>(rows: T[]) =>\n assertSQLQueryResultMatches(sql, rows),\n },\n }),\n};\n","import {\n type Dumbo,\n type NodePostgresClient,\n type NodePostgresTransaction,\n type SQL,\n type SQLExecutor,\n} from '@event-driven-io/dumbo';\nimport {\n projection,\n type CanHandle,\n type Event,\n type ProjectionDefinition,\n type ProjectionHandler,\n type ReadEvent,\n} from '@event-driven-io/emmett';\nimport type { PostgresReadEventMetadata } from '../postgreSQLEventStore';\n\nexport type PostgreSQLProjectionHandlerContext = {\n execute: SQLExecutor;\n connection: {\n connectionString: string;\n client: NodePostgresClient;\n transaction: NodePostgresTransaction;\n pool: Dumbo;\n };\n};\n\nexport type PostgreSQLProjectionHandler<\n EventType extends Event = Event,\n EventMetaDataType extends\n PostgresReadEventMetadata = PostgresReadEventMetadata,\n> = ProjectionHandler<\n EventType,\n EventMetaDataType,\n PostgreSQLProjectionHandlerContext\n>;\n\nexport type PostgreSQLProjectionDefinition<EventType extends Event = Event> =\n ProjectionDefinition<\n EventType,\n PostgresReadEventMetadata,\n PostgreSQLProjectionHandlerContext\n >;\n\nexport type PostgreSQLProjectionHandlerOptions<\n EventType extends Event = Event,\n> = {\n events: ReadEvent<EventType, PostgresReadEventMetadata>[];\n projections: PostgreSQLProjectionDefinition<EventType>[];\n connection: {\n connectionString: string;\n transaction: NodePostgresTransaction;\n pool: Dumbo;\n };\n};\n\nexport const handleProjections = async <EventType extends Event = Event>(\n options: PostgreSQLProjectionHandlerOptions<EventType>,\n): Promise<void> => {\n const {\n projections: allProjections,\n events,\n connection: { pool, transaction, connectionString },\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 const client = (await transaction.connection.open()) as NodePostgresClient;\n\n for (const projection of projections) {\n await projection.handle(events, {\n connection: {\n connectionString,\n pool,\n client,\n transaction,\n },\n execute: transaction.execute,\n });\n }\n};\n\nexport const postgreSQLProjection = <EventType extends Event>(\n definition: PostgreSQLProjectionDefinition<EventType>,\n): PostgreSQLProjectionDefinition<EventType> =>\n projection<\n EventType,\n PostgresReadEventMetadata,\n PostgreSQLProjectionHandlerContext\n >(definition);\n\nexport const postgreSQLRawBatchSQLProjection = <EventType extends Event>(\n handle: (\n events: EventType[],\n context: PostgreSQLProjectionHandlerContext,\n ) => Promise<SQL[]> | SQL[],\n ...canHandle: CanHandle<EventType>\n): PostgreSQLProjectionDefinition<EventType> =>\n postgreSQLProjection<EventType>({\n canHandle,\n handle: async (events, context) => {\n const sqls: SQL[] = await handle(events, context);\n\n await context.execute.batchCommand(sqls);\n },\n });\n\nexport const postgreSQLRawSQLProjection = <EventType extends Event>(\n handle: (\n event: EventType,\n context: PostgreSQLProjectionHandlerContext,\n ) => Promise<SQL> | SQL,\n ...canHandle: CanHandle<EventType>\n): PostgreSQLProjectionDefinition<EventType> =>\n postgreSQLRawBatchSQLProjection<EventType>(\n async (events, context) => {\n const sqls: SQL[] = [];\n\n for (const event of events) {\n sqls.push(await handle(event, context));\n }\n return sqls;\n },\n ...canHandle,\n );\n"]}
1
+ {"version":3,"sources":["/home/runner/work/emmett/emmett/src/packages/emmett-postgresql/dist/index.cjs","../src/eventStore/postgreSQLEventStore.ts","../../emmett/src/validation/index.ts","../../emmett/src/errors/index.ts","../../emmett/src/eventStore/inMemoryEventStore.ts","../../emmett/src/database/inMemoryDatabase.ts","../../emmett/src/utils/retry.ts","../../emmett/src/eventStore/projections/inMemory/inMemoryProjectionSpec.ts","../../emmett/src/eventStore/subscriptions/caughtUpTransformStream.ts","../../emmett/src/eventStore/subscriptions/streamingCoordinator.ts","../../emmett/src/streaming/transformations/notifyAboutNoActiveReaders.ts","../../emmett/src/streaming/generators/fromArray.ts","../../emmett/src/streaming/restream.ts","../../emmett/src/streaming/transformations/filter.ts","../../emmett/src/streaming/transformations/map.ts","../../emmett/src/streaming/transformations/reduce.ts","../../emmett/src/streaming/transformations/retry.ts","../../emmett/src/streaming/transformations/skip.ts","../../emmett/src/streaming/transformations/stopAfter.ts","../../emmett/src/streaming/transformations/stopOn.ts","../../emmett/src/streaming/transformations/take.ts","../../emmett/src/streaming/transformations/waitAtMost.ts","../../emmett/src/eventStore/expectedVersion.ts","../../emmett/src/utils/deepEquals.ts","../../emmett/src/serialization/json/JSONParser.ts","../../emmett/src/testing/assertions.ts","../../emmett/src/processors/processors.ts","../../emmett/src/projections/index.ts","../../emmett/src/streaming/transformations/index.ts","../src/eventStore/consumers/messageBatchProcessing/index.ts","../src/eventStore/schema/readLastMessageGlobalPosition.ts","../src/eventStore/schema/typing.ts","../src/eventStore/schema/readMessagesBatch.ts","../src/eventStore/consumers/postgreSQLEventStoreConsumer.ts","../src/eventStore/consumers/postgreSQLProcessor.ts","../src/eventStore/schema/index.ts","../src/eventStore/schema/appendToStream.ts","../src/eventStore/schema/storeProcessorCheckpoint.ts","../src/eventStore/schema/tables.ts","../src/eventStore/schema/readProcessorCheckpoint.ts","../src/eventStore/schema/readStream.ts","../src/eventStore/consumers/rebuildPostgreSQLProjections.ts","../src/eventStore/projections/pongo/pongoProjections.ts","../src/eventStore/projections/pongo/pongoProjectionSpec.ts","../src/eventStore/projections/postgresProjectionSpec.ts","../src/eventStore/projections/postgreSQLProjection.ts"],"names":[],"mappings":"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","file":"/home/runner/work/emmett/emmett/src/packages/emmett-postgresql/dist/index.cjs","sourcesContent":[null,"import {\n dumbo,\n type MigrationStyle,\n type NodePostgresClientConnection,\n type NodePostgresConnector,\n type NodePostgresPool,\n type NodePostgresPoolClientConnection,\n} from '@event-driven-io/dumbo';\nimport {\n assertExpectedVersionMatchesCurrent,\n ExpectedVersionConflictError,\n NO_CONCURRENCY_CHECK,\n type AggregateStreamOptions,\n type AggregateStreamResult,\n type AppendToStreamOptions,\n type AppendToStreamResultWithGlobalPosition,\n type Event,\n type EventStore,\n type EventStoreSession,\n type EventStoreSessionFactory,\n type ProjectionRegistration,\n type ReadEvent,\n type ReadEventMetadataWithGlobalPosition,\n type ReadStreamOptions,\n type ReadStreamResult,\n} from '@event-driven-io/emmett';\nimport pg from 'pg';\nimport {\n postgreSQLEventStoreConsumer,\n type PostgreSQLEventStoreConsumer,\n type PostgreSQLEventStoreConsumerConfig,\n} from './consumers';\nimport {\n handleProjections,\n type PostgreSQLProjectionHandlerContext,\n} from './projections';\nimport {\n appendToStream,\n createEventStoreSchema,\n readStream,\n schemaSQL,\n type AppendToStreamBeforeCommitHook,\n} from './schema';\n\nexport interface PostgresEventStore\n extends EventStore<PostgresReadEventMetadata>,\n EventStoreSessionFactory<PostgresEventStore> {\n appendToStream<EventType extends Event>(\n streamName: string,\n events: EventType[],\n options?: AppendToStreamOptions,\n ): Promise<AppendToStreamResultWithGlobalPosition>;\n consumer<ConsumerEventType extends Event = Event>(\n options?: PostgreSQLEventStoreConsumerConfig<ConsumerEventType>,\n ): PostgreSQLEventStoreConsumer<ConsumerEventType>;\n close(): Promise<void>;\n schema: {\n sql(): string;\n print(): void;\n migrate(): Promise<void>;\n };\n}\n\nexport type PostgresReadEventMetadata = ReadEventMetadataWithGlobalPosition;\n\nexport type PostgresReadEvent<EventType extends Event = Event> = ReadEvent<\n EventType,\n PostgresReadEventMetadata\n>;\n\ntype PostgresEventStorePooledOptions =\n | {\n connector?: NodePostgresConnector;\n connectionString?: string;\n database?: string;\n pooled: true;\n pool: pg.Pool;\n }\n | {\n connector?: NodePostgresConnector;\n connectionString?: string;\n database?: string;\n pool: pg.Pool;\n }\n | {\n connector?: NodePostgresConnector;\n connectionString?: string;\n database?: string;\n pooled: true;\n }\n | {\n connector?: NodePostgresConnector;\n connectionString?: string;\n database?: string;\n };\n\ntype PostgresEventStoreNotPooledOptions =\n | {\n connector?: NodePostgresConnector;\n connectionString?: string;\n database?: string;\n pooled: false;\n client: pg.Client;\n }\n | {\n connector?: NodePostgresConnector;\n connectionString?: string;\n database?: string;\n client: pg.Client;\n }\n | {\n connector?: NodePostgresConnector;\n connectionString?: string;\n database?: string;\n pooled: false;\n }\n | {\n connector?: NodePostgresConnector;\n connectionString?: string;\n database?: string;\n connection:\n | NodePostgresPoolClientConnection\n | NodePostgresClientConnection;\n pooled?: false;\n }\n | {\n connector?: NodePostgresConnector;\n connectionString?: string;\n database?: string;\n dumbo: NodePostgresPool;\n pooled?: false;\n };\n\nexport type PostgresEventStoreConnectionOptions =\n | PostgresEventStorePooledOptions\n | PostgresEventStoreNotPooledOptions;\n\nexport type PostgresEventStoreOptions = {\n projections?: ProjectionRegistration<\n 'inline',\n PostgresReadEventMetadata,\n PostgreSQLProjectionHandlerContext\n >[];\n schema?: { autoMigration?: MigrationStyle };\n connectionOptions?: PostgresEventStoreConnectionOptions;\n hooks?: {\n /**\n * This hook will be called **AFTER** event store schema was created\n */\n onAfterSchemaCreated?: () => Promise<void> | void;\n };\n};\n\nexport const defaultPostgreSQLOptions: PostgresEventStoreOptions = {\n projections: [],\n schema: { autoMigration: 'CreateOrUpdate' },\n};\n\nexport const PostgreSQLEventStoreDefaultStreamVersion = 0n;\n\nexport const getPostgreSQLEventStore = (\n connectionString: string,\n options: PostgresEventStoreOptions = defaultPostgreSQLOptions,\n): PostgresEventStore => {\n const poolOptions = {\n connectionString,\n ...(options.connectionOptions ? options.connectionOptions : {}),\n };\n const pool = 'dumbo' in poolOptions ? poolOptions.dumbo : dumbo(poolOptions);\n let migrateSchema: Promise<void> | undefined = undefined;\n\n const autoGenerateSchema =\n options.schema?.autoMigration === undefined ||\n options.schema?.autoMigration !== 'None';\n\n const ensureSchemaExists = () => {\n if (!autoGenerateSchema) return Promise.resolve();\n\n if (!migrateSchema) {\n migrateSchema = createEventStoreSchema(pool).then(async () => {\n if (options.hooks?.onAfterSchemaCreated) {\n await options.hooks.onAfterSchemaCreated();\n }\n });\n }\n return migrateSchema;\n };\n\n const inlineProjections = (options.projections ?? [])\n .filter(({ type }) => type === 'inline')\n .map(({ projection }) => projection);\n\n const beforeCommitHook: AppendToStreamBeforeCommitHook | undefined =\n inlineProjections.length > 0\n ? (events, { transaction }) =>\n handleProjections({\n projections: inlineProjections,\n connection: {\n connectionString,\n pool,\n transaction,\n },\n // TODO: Add proper handling of global data\n // Currently it's not available as append doesn't return array of global position but just the last one\n events: events as ReadEvent<Event, PostgresReadEventMetadata>[],\n })\n : undefined;\n\n return {\n schema: {\n sql: () => schemaSQL.join(''),\n print: () => console.log(schemaSQL.join('')),\n migrate: async () => {\n await (migrateSchema = createEventStoreSchema(pool));\n },\n },\n async aggregateStream<State, EventType extends Event>(\n streamName: string,\n options: AggregateStreamOptions<\n State,\n EventType,\n PostgresReadEventMetadata\n >,\n ): Promise<AggregateStreamResult<State>> {\n const { evolve, initialState, read } = options;\n\n const expectedStreamVersion = read?.expectedStreamVersion;\n\n let state = initialState();\n\n const result = await this.readStream<EventType>(streamName, options.read);\n const currentStreamVersion = result.currentStreamVersion;\n\n assertExpectedVersionMatchesCurrent(\n currentStreamVersion,\n expectedStreamVersion,\n PostgreSQLEventStoreDefaultStreamVersion,\n );\n\n for (const event of result.events) {\n if (!event) continue;\n\n state = evolve(state, event);\n }\n\n return {\n currentStreamVersion: currentStreamVersion,\n state,\n streamExists: result.streamExists,\n };\n },\n\n readStream: async <EventType extends Event>(\n streamName: string,\n options?: ReadStreamOptions,\n ): Promise<ReadStreamResult<EventType, PostgresReadEventMetadata>> => {\n await ensureSchemaExists();\n return readStream<EventType>(pool.execute, streamName, options);\n },\n\n appendToStream: async <EventType extends Event>(\n streamName: string,\n events: EventType[],\n options?: AppendToStreamOptions,\n ): Promise<AppendToStreamResultWithGlobalPosition> => {\n await ensureSchemaExists();\n // TODO: This has to be smarter when we introduce urn-based resolution\n const [firstPart, ...rest] = streamName.split('-');\n\n const streamType =\n firstPart && rest.length > 0 ? firstPart : 'emt:unknown';\n\n const appendResult = await appendToStream(\n pool,\n streamName,\n streamType,\n events,\n {\n ...options,\n beforeCommitHook,\n },\n );\n\n if (!appendResult.success)\n throw new ExpectedVersionConflictError<bigint>(\n -1n, //TODO: Return actual version in case of error\n options?.expectedStreamVersion ?? NO_CONCURRENCY_CHECK,\n );\n\n return {\n nextExpectedStreamVersion: appendResult.nextStreamPosition,\n lastEventGlobalPosition:\n appendResult.globalPositions[\n appendResult.globalPositions.length - 1\n ]!,\n createdNewStream:\n appendResult.nextStreamPosition >= BigInt(events.length),\n };\n },\n consumer: <ConsumerEventType extends Event = Event>(\n options?: PostgreSQLEventStoreConsumerConfig<ConsumerEventType>,\n ): PostgreSQLEventStoreConsumer<ConsumerEventType> =>\n postgreSQLEventStoreConsumer<ConsumerEventType>({\n ...(options ?? {}),\n pool,\n connectionString,\n }),\n close: () => pool.close(),\n\n async withSession<T = unknown>(\n callback: (session: EventStoreSession<PostgresEventStore>) => Promise<T>,\n ): Promise<T> {\n return await pool.withConnection(async (connection) => {\n const storeOptions: PostgresEventStoreOptions = {\n ...options,\n connectionOptions: {\n connection,\n },\n schema: {\n ...(options.schema ?? {}),\n autoMigration: 'None',\n },\n };\n\n const eventStore = getPostgreSQLEventStore(\n connectionString,\n storeOptions,\n );\n\n return ensureSchemaExists().then(() =>\n callback({\n eventStore,\n close: () => Promise.resolve(),\n }),\n );\n });\n },\n };\n};\n","import { ValidationError } from '../errors';\n\nexport const enum ValidationErrors {\n NOT_A_NONEMPTY_STRING = 'NOT_A_NONEMPTY_STRING',\n NOT_A_POSITIVE_NUMBER = 'NOT_A_POSITIVE_NUMBER',\n NOT_AN_UNSIGNED_BIGINT = 'NOT_AN_UNSIGNED_BIGINT',\n}\n\nexport const isNumber = (val: unknown): val is number =>\n typeof val === 'number' && val === val;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport const isBigint = (val: any): val is bigint =>\n typeof val === 'bigint' && val === val;\n\nexport const isString = (val: unknown): val is string =>\n typeof val === 'string';\n\nexport const assertNotEmptyString = (value: unknown): string => {\n if (!isString(value) || value.length === 0) {\n throw new ValidationError(ValidationErrors.NOT_A_NONEMPTY_STRING);\n }\n return value;\n};\n\nexport const assertPositiveNumber = (value: unknown): number => {\n if (!isNumber(value) || value <= 0) {\n throw new ValidationError(ValidationErrors.NOT_A_POSITIVE_NUMBER);\n }\n return value;\n};\n\nexport const assertUnsignedBigInt = (value: string): bigint => {\n const number = BigInt(value);\n if (number < 0) {\n throw new ValidationError(ValidationErrors.NOT_AN_UNSIGNED_BIGINT);\n }\n return number;\n};\n\nexport * from './dates';\n","import { isNumber, isString } from '../validation';\n\nexport type ErrorConstructor<ErrorType extends Error> = new (\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ...args: any[]\n) => ErrorType;\n\nexport const isErrorConstructor = <ErrorType extends Error>(\n // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n expect: Function,\n): expect is ErrorConstructor<ErrorType> => {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return (\n typeof expect === 'function' &&\n expect.prototype &&\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n expect.prototype.constructor === expect\n );\n};\n\nexport class EmmettError extends Error {\n public errorCode: number;\n\n constructor(\n options?: { errorCode: number; message?: string } | string | number,\n ) {\n const errorCode =\n options && typeof options === 'object' && 'errorCode' in options\n ? options.errorCode\n : isNumber(options)\n ? options\n : 500;\n const message =\n options && typeof options === 'object' && 'message' in options\n ? options.message\n : isString(options)\n ? options\n : `Error with status code '${errorCode}' ocurred during Emmett processing`;\n\n super(message);\n this.errorCode = errorCode;\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, EmmettError.prototype);\n }\n\n public static mapFrom(\n error: Error | { message?: string; errorCode?: number },\n ): EmmettError {\n if (error instanceof EmmettError) {\n return error;\n }\n\n return new EmmettError({\n errorCode:\n 'errorCode' in error &&\n error.errorCode !== undefined &&\n error.errorCode !== null\n ? error.errorCode\n : 500,\n message: error.message ?? 'An unknown error occurred',\n });\n }\n}\n\nexport class ConcurrencyError extends EmmettError {\n constructor(\n public current: string | undefined,\n public expected: string,\n message?: string,\n ) {\n super({\n errorCode: 412,\n message:\n message ??\n `Expected version ${expected.toString()} does not match current ${current?.toString()}`,\n });\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, ConcurrencyError.prototype);\n }\n}\n\nexport class ConcurrencyInMemoryDatabaseError extends EmmettError {\n constructor(message?: string) {\n super({\n errorCode: 412,\n message: message ?? `Expected document state does not match current one!`,\n });\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, ConcurrencyInMemoryDatabaseError.prototype);\n }\n}\n\nexport class ValidationError extends EmmettError {\n constructor(message?: string) {\n super({\n errorCode: 400,\n message: message ?? `Validation Error ocurred during Emmett processing`,\n });\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, ValidationError.prototype);\n }\n}\n\nexport class IllegalStateError extends EmmettError {\n constructor(message?: string) {\n super({\n errorCode: 403,\n message: message ?? `Illegal State ocurred during Emmett processing`,\n });\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, IllegalStateError.prototype);\n }\n}\n\nexport class NotFoundError extends EmmettError {\n constructor(options?: { id: string; type: string; message?: string }) {\n super({\n errorCode: 404,\n message:\n options?.message ??\n (options?.id\n ? options.type\n ? `${options.type} with ${options.id} was not found during Emmett processing`\n : `State with ${options.id} was not found during Emmett processing`\n : options?.type\n ? `${options.type} was not found during Emmett processing`\n : 'State was not found during Emmett processing'),\n });\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, NotFoundError.prototype);\n }\n}\n","import { v4 as uuid } from 'uuid';\nimport {\n getInMemoryDatabase,\n type InMemoryDatabase,\n} from '../database/inMemoryDatabase';\nimport type { ProjectionRegistration } from '../projections';\nimport type {\n BigIntStreamPosition,\n CombinedReadEventMetadata,\n Event,\n ReadEvent,\n ReadEventMetadataWithGlobalPosition,\n} from '../typing';\nimport { tryPublishMessagesAfterCommit } from './afterCommit';\nimport {\n type AggregateStreamOptions,\n type AggregateStreamResult,\n type AppendToStreamOptions,\n type AppendToStreamResult,\n type DefaultEventStoreOptions,\n type EventStore,\n type ReadStreamOptions,\n type ReadStreamResult,\n} from './eventStore';\nimport { assertExpectedVersionMatchesCurrent } from './expectedVersion';\nimport { handleInMemoryProjections } from './projections/inMemory';\nimport { StreamingCoordinator } from './subscriptions';\n\nexport const InMemoryEventStoreDefaultStreamVersion = 0n;\n\nexport type InMemoryEventStore =\n EventStore<ReadEventMetadataWithGlobalPosition> & {\n database: InMemoryDatabase;\n };\n\nexport type InMemoryReadEventMetadata = ReadEventMetadataWithGlobalPosition;\n\nexport type InMemoryProjectionHandlerContext = {\n eventStore?: InMemoryEventStore;\n database?: InMemoryDatabase;\n};\n\nexport type InMemoryEventStoreOptions =\n DefaultEventStoreOptions<InMemoryEventStore> & {\n projections?: ProjectionRegistration<\n 'inline',\n InMemoryReadEventMetadata,\n InMemoryProjectionHandlerContext\n >[];\n database?: InMemoryDatabase;\n };\n\nexport type InMemoryReadEvent<EventType extends Event = Event> = ReadEvent<\n EventType,\n ReadEventMetadataWithGlobalPosition\n>;\n\nexport const getInMemoryEventStore = (\n eventStoreOptions?: InMemoryEventStoreOptions,\n): InMemoryEventStore => {\n const streams = new Map<\n string,\n ReadEvent<Event, ReadEventMetadataWithGlobalPosition>[]\n >();\n const streamingCoordinator = StreamingCoordinator();\n\n const getAllEventsCount = () => {\n return Array.from<ReadEvent[]>(streams.values())\n .map((s) => s.length)\n .reduce((p, c) => p + c, 0);\n };\n\n // Get the database instance to be used for projections\n const database = eventStoreOptions?.database || getInMemoryDatabase();\n\n // Extract inline projections from options\n const inlineProjections = (eventStoreOptions?.projections ?? [])\n .filter(({ type }) => type === 'inline')\n .map(({ projection }) => projection);\n\n // Create the event store object\n const eventStore: InMemoryEventStore = {\n database,\n async aggregateStream<State, EventType extends Event>(\n streamName: string,\n options: AggregateStreamOptions<\n State,\n EventType,\n ReadEventMetadataWithGlobalPosition\n >,\n ): Promise<AggregateStreamResult<State>> {\n const { evolve, initialState, read } = options;\n\n const result = await this.readStream<EventType>(streamName, read);\n\n const events = result?.events ?? [];\n\n return {\n currentStreamVersion: BigInt(events.length),\n state: events.reduce(evolve, initialState()),\n streamExists: result.streamExists,\n };\n },\n\n readStream: <EventType extends Event>(\n streamName: string,\n options?: ReadStreamOptions<BigIntStreamPosition>,\n ): Promise<\n ReadStreamResult<EventType, ReadEventMetadataWithGlobalPosition>\n > => {\n const events = streams.get(streamName);\n const currentStreamVersion = events\n ? BigInt(events.length)\n : InMemoryEventStoreDefaultStreamVersion;\n\n assertExpectedVersionMatchesCurrent(\n currentStreamVersion,\n options?.expectedStreamVersion,\n InMemoryEventStoreDefaultStreamVersion,\n );\n\n const from = Number(options && 'from' in options ? options.from : 0);\n const to = Number(\n options && 'to' in options\n ? options.to\n : options && 'maxCount' in options && options.maxCount\n ? options.from + options.maxCount\n : (events?.length ?? 1),\n );\n\n const resultEvents =\n events !== undefined && events.length > 0\n ? events\n .map(\n (e) =>\n e as ReadEvent<\n EventType,\n ReadEventMetadataWithGlobalPosition\n >,\n )\n .slice(from, to)\n : [];\n\n const result: ReadStreamResult<\n EventType,\n ReadEventMetadataWithGlobalPosition\n > = {\n currentStreamVersion,\n events: resultEvents,\n streamExists: events !== undefined && events.length > 0,\n };\n\n return Promise.resolve(result);\n },\n\n appendToStream: async <EventType extends Event>(\n streamName: string,\n events: EventType[],\n options?: AppendToStreamOptions,\n ): Promise<AppendToStreamResult> => {\n const currentEvents = streams.get(streamName) ?? [];\n const currentStreamVersion =\n currentEvents.length > 0\n ? BigInt(currentEvents.length)\n : InMemoryEventStoreDefaultStreamVersion;\n\n assertExpectedVersionMatchesCurrent(\n currentStreamVersion,\n options?.expectedStreamVersion,\n InMemoryEventStoreDefaultStreamVersion,\n );\n\n const newEvents: ReadEvent<\n EventType,\n ReadEventMetadataWithGlobalPosition\n >[] = events.map((event, index) => {\n const metadata: ReadEventMetadataWithGlobalPosition = {\n streamName,\n messageId: uuid(),\n streamPosition: BigInt(currentEvents.length + index + 1),\n globalPosition: BigInt(getAllEventsCount() + index + 1),\n };\n return {\n ...event,\n kind: event.kind ?? 'Event',\n metadata: {\n ...('metadata' in event ? (event.metadata ?? {}) : {}),\n ...metadata,\n } as CombinedReadEventMetadata<\n EventType,\n ReadEventMetadataWithGlobalPosition\n >,\n };\n });\n\n const positionOfLastEventInTheStream = BigInt(\n newEvents.slice(-1)[0]!.metadata.streamPosition,\n );\n\n streams.set(streamName, [...currentEvents, ...newEvents]);\n await streamingCoordinator.notify(newEvents);\n\n // Process projections if there are any registered\n if (inlineProjections.length > 0) {\n await handleInMemoryProjections({\n projections: inlineProjections,\n events: newEvents,\n database: eventStore.database,\n eventStore,\n });\n }\n\n const result: AppendToStreamResult = {\n nextExpectedStreamVersion: positionOfLastEventInTheStream,\n createdNewStream:\n currentStreamVersion === InMemoryEventStoreDefaultStreamVersion,\n };\n\n await tryPublishMessagesAfterCommit<InMemoryEventStore>(\n newEvents,\n eventStoreOptions?.hooks,\n );\n\n return result;\n },\n\n //streamEvents: streamingCoordinator.stream,\n };\n\n return eventStore;\n};\n","import { v7 as uuid } from 'uuid';\nimport { deepEquals } from '../utils';\nimport {\n type DatabaseHandleOptionErrors,\n type DatabaseHandleOptions,\n type DatabaseHandleResult,\n type DeleteResult,\n type Document,\n type DocumentHandler,\n type InsertOneResult,\n type OptionalUnlessRequiredIdAndVersion,\n type ReplaceOneOptions,\n type UpdateResult,\n type WithIdAndVersion,\n type WithoutId,\n} from './types';\nimport { expectedVersionValue, operationResult } from './utils';\n\nexport interface InMemoryDocumentsCollection<T extends Document> {\n handle: (\n id: string,\n handle: DocumentHandler<T>,\n options?: DatabaseHandleOptions,\n ) => Promise<DatabaseHandleResult<T>>;\n findOne: (predicate?: Predicate<T>) => Promise<T | null>;\n find: (predicate?: Predicate<T>) => Promise<T[]>;\n insertOne: (\n document: OptionalUnlessRequiredIdAndVersion<T>,\n ) => Promise<InsertOneResult>;\n deleteOne: (predicate?: Predicate<T>) => Promise<DeleteResult>;\n replaceOne: (\n predicate: Predicate<T>,\n document: WithoutId<T>,\n options?: ReplaceOneOptions,\n ) => Promise<UpdateResult>;\n}\n\nexport interface InMemoryDatabase {\n collection: <T extends Document>(\n name: string,\n ) => InMemoryDocumentsCollection<T>;\n}\n\ntype Predicate<T> = (item: T) => boolean;\ntype CollectionName = string;\n\nexport const getInMemoryDatabase = (): InMemoryDatabase => {\n const storage = new Map<CollectionName, WithIdAndVersion<Document>[]>();\n\n return {\n collection: <T extends Document, CollectionName extends string>(\n collectionName: CollectionName,\n collectionOptions: {\n errors?: DatabaseHandleOptionErrors;\n } = {},\n ): InMemoryDocumentsCollection<T> => {\n const ensureCollectionCreated = () => {\n if (!storage.has(collectionName)) storage.set(collectionName, []);\n };\n\n const errors = collectionOptions.errors;\n\n const collection = {\n collectionName,\n insertOne: async (\n document: OptionalUnlessRequiredIdAndVersion<T>,\n ): Promise<InsertOneResult> => {\n ensureCollectionCreated();\n\n const _id = (document._id as string | undefined | null) ?? uuid();\n const _version = document._version ?? 1n;\n\n const existing = await collection.findOne((c) => c._id === _id);\n\n if (existing) {\n return operationResult<InsertOneResult>(\n {\n successful: false,\n insertedId: null,\n nextExpectedVersion: _version,\n },\n { operationName: 'insertOne', collectionName, errors },\n );\n }\n\n const documentsInCollection = storage.get(collectionName)!;\n const newDocument = { ...document, _id, _version };\n const newCollection = [...documentsInCollection, newDocument];\n storage.set(collectionName, newCollection);\n\n return operationResult<InsertOneResult>(\n {\n successful: true,\n insertedId: _id,\n nextExpectedVersion: _version,\n },\n { operationName: 'insertOne', collectionName, errors },\n );\n },\n findOne: (predicate?: Predicate<T>): Promise<T | null> => {\n ensureCollectionCreated();\n\n const documentsInCollection = storage.get(collectionName);\n const filteredDocuments = predicate\n ? documentsInCollection?.filter((doc) => predicate(doc as T))\n : documentsInCollection;\n\n const firstOne = filteredDocuments?.[0] ?? null;\n\n return Promise.resolve(firstOne as T | null);\n },\n find: (predicate?: Predicate<T>): Promise<T[]> => {\n ensureCollectionCreated();\n\n const documentsInCollection = storage.get(collectionName);\n const filteredDocuments = predicate\n ? documentsInCollection?.filter((doc) => predicate(doc as T))\n : documentsInCollection;\n\n return Promise.resolve(filteredDocuments as T[]);\n },\n deleteOne: (predicate?: Predicate<T>): Promise<DeleteResult> => {\n ensureCollectionCreated();\n\n const documentsInCollection = storage.get(collectionName)!;\n\n if (predicate) {\n const foundIndex = documentsInCollection.findIndex((doc) =>\n predicate(doc as T),\n );\n\n if (foundIndex === -1) {\n return Promise.resolve(\n operationResult<DeleteResult>(\n {\n successful: false,\n matchedCount: 0,\n deletedCount: 0,\n },\n { operationName: 'deleteOne', collectionName, errors },\n ),\n );\n } else {\n const newCollection = documentsInCollection.toSpliced(\n foundIndex,\n 1,\n );\n\n storage.set(collectionName, newCollection);\n\n return Promise.resolve(\n operationResult<DeleteResult>(\n {\n successful: true,\n matchedCount: 1,\n deletedCount: 1,\n },\n { operationName: 'deleteOne', collectionName, errors },\n ),\n );\n }\n }\n\n const newCollection = documentsInCollection.slice(1);\n\n storage.set(collectionName, newCollection);\n\n return Promise.resolve(\n operationResult<DeleteResult>(\n {\n successful: true,\n matchedCount: 1,\n deletedCount: 1,\n },\n { operationName: 'deleteOne', collectionName, errors },\n ),\n );\n },\n replaceOne: (\n predicate: Predicate<T>,\n document: WithoutId<T>,\n options?: ReplaceOneOptions,\n ): Promise<UpdateResult> => {\n ensureCollectionCreated();\n\n const documentsInCollection = storage.get(collectionName)!;\n\n const firstIndex = documentsInCollection.findIndex((doc) =>\n predicate(doc as T),\n );\n\n if (firstIndex === undefined || firstIndex === -1) {\n return Promise.resolve(\n operationResult<UpdateResult>(\n {\n successful: false,\n matchedCount: 0,\n modifiedCount: 0,\n nextExpectedVersion: 0n,\n },\n { operationName: 'replaceOne', collectionName, errors },\n ),\n );\n }\n\n const existing = documentsInCollection[firstIndex]!;\n\n if (\n typeof options?.expectedVersion === 'bigint' &&\n existing._version !== options.expectedVersion\n ) {\n return Promise.resolve(\n operationResult<UpdateResult>(\n {\n successful: false,\n matchedCount: 1,\n modifiedCount: 0,\n nextExpectedVersion: existing._version,\n },\n { operationName: 'replaceOne', collectionName, errors },\n ),\n );\n }\n\n const newVersion = existing._version + 1n;\n\n const newCollection = documentsInCollection.with(firstIndex, {\n _id: existing._id,\n ...document,\n _version: newVersion,\n });\n\n storage.set(collectionName, newCollection);\n\n return Promise.resolve(\n operationResult<UpdateResult>(\n {\n successful: true,\n modifiedCount: 1,\n matchedCount: firstIndex,\n nextExpectedVersion: newVersion,\n },\n { operationName: 'replaceOne', collectionName, errors },\n ),\n );\n },\n handle: async (\n id: string,\n handle: DocumentHandler<T>,\n options?: DatabaseHandleOptions,\n ): Promise<DatabaseHandleResult<T>> => {\n const { expectedVersion: version, ...operationOptions } =\n options ?? {};\n ensureCollectionCreated();\n const existing = await collection.findOne(({ _id }) => _id === id);\n\n const expectedVersion = expectedVersionValue(version);\n\n if (\n (existing == null && version === 'DOCUMENT_EXISTS') ||\n (existing == null && expectedVersion != null) ||\n (existing != null && version === 'DOCUMENT_DOES_NOT_EXIST') ||\n (existing != null &&\n expectedVersion !== null &&\n existing._version !== expectedVersion)\n ) {\n return operationResult<DatabaseHandleResult<T>>(\n {\n successful: false,\n document: existing as WithIdAndVersion<T>,\n },\n { operationName: 'handle', collectionName, errors },\n );\n }\n\n const result = handle(existing !== null ? { ...existing } : null);\n\n if (deepEquals(existing, result))\n return operationResult<DatabaseHandleResult<T>>(\n {\n successful: true,\n document: existing as WithIdAndVersion<T>,\n },\n { operationName: 'handle', collectionName, errors },\n );\n\n if (!existing && result) {\n const newDoc = { ...result, _id: id };\n const insertResult = await collection.insertOne({\n ...newDoc,\n _id: id,\n } as OptionalUnlessRequiredIdAndVersion<T>);\n return {\n ...insertResult,\n document: {\n ...newDoc,\n _version: insertResult.nextExpectedVersion,\n } as unknown as WithIdAndVersion<T>,\n };\n }\n\n if (existing && !result) {\n const deleteResult = await collection.deleteOne(\n ({ _id }) => id === _id,\n );\n return { ...deleteResult, document: null };\n }\n\n if (existing && result) {\n const replaceResult = await collection.replaceOne(\n ({ _id }) => id === _id,\n result,\n {\n ...operationOptions,\n expectedVersion: expectedVersion ?? 'DOCUMENT_EXISTS',\n },\n );\n return {\n ...replaceResult,\n document: {\n ...result,\n _version: replaceResult.nextExpectedVersion,\n } as unknown as WithIdAndVersion<T>,\n };\n }\n\n return operationResult<DatabaseHandleResult<T>>(\n {\n successful: true,\n document: existing as WithIdAndVersion<T>,\n },\n { operationName: 'handle', collectionName, errors },\n );\n },\n };\n\n return collection;\n },\n };\n};\n","import retry from 'async-retry';\nimport { EmmettError } from '../errors';\nimport { JSONParser } from '../serialization';\n\nexport type AsyncRetryOptions<T = unknown> = retry.Options & {\n shouldRetryResult?: (result: T) => boolean;\n shouldRetryError?: (error?: unknown) => boolean;\n};\n\nexport const NoRetries: AsyncRetryOptions = { retries: 0 };\n\nexport const asyncRetry = async <T>(\n fn: () => Promise<T>,\n opts?: AsyncRetryOptions<T>,\n): Promise<T> => {\n if (opts === undefined || opts.retries === 0) return fn();\n\n return retry(\n async (bail) => {\n try {\n const result = await fn();\n\n if (opts?.shouldRetryResult && opts.shouldRetryResult(result)) {\n throw new EmmettError(\n `Retrying because of result: ${JSONParser.stringify(result)}`,\n );\n }\n return result;\n } catch (error) {\n if (opts?.shouldRetryError && !opts.shouldRetryError(error)) {\n bail(error as Error);\n return undefined as unknown as T;\n }\n throw error;\n }\n },\n opts ?? { retries: 0 },\n );\n};\n","import { v4 as uuid } from 'uuid';\nimport {\n handleInMemoryProjections,\n type InMemoryProjectionDefinition,\n} from '.';\nimport {\n getInMemoryDatabase,\n type Document,\n type InMemoryDatabase,\n} from '../../../database';\nimport { isErrorConstructor } from '../../../errors';\nimport { JSONParser } from '../../../serialization';\nimport {\n assertFails,\n AssertionError,\n assertTrue,\n type ThenThrows,\n} from '../../../testing';\nimport type { CombinedReadEventMetadata, ReadEvent } from '../../../typing';\nimport { type Event } from '../../../typing';\nimport type {\n InMemoryEventStore,\n InMemoryReadEventMetadata,\n} from '../../inMemoryEventStore';\n\n// Define a more specific type for T that extends Document\ntype DocumentWithId = Document & { _id?: string | number };\n\nexport type InMemoryProjectionSpecEvent<\n EventType extends Event,\n EventMetaDataType extends\n InMemoryReadEventMetadata = InMemoryReadEventMetadata,\n> = EventType & {\n metadata?: Partial<EventMetaDataType>;\n};\n\nexport type InMemoryProjectionSpecWhenOptions = { numberOfTimes: number };\n\nexport type InMemoryProjectionSpec<EventType extends Event> = (\n givenEvents: InMemoryProjectionSpecEvent<EventType>[],\n) => {\n when: (\n events: InMemoryProjectionSpecEvent<EventType>[],\n options?: InMemoryProjectionSpecWhenOptions,\n ) => {\n then: (assert: InMemoryProjectionAssert, message?: string) => Promise<void>;\n thenThrows: <ErrorType extends Error = Error>(\n ...args: Parameters<ThenThrows<ErrorType>>\n ) => Promise<void>;\n };\n};\n\nexport type InMemoryProjectionAssert = (options: {\n database: InMemoryDatabase;\n}) => Promise<void | boolean>;\n\nexport type InMemoryProjectionSpecOptions<EventType extends Event> = {\n projection: InMemoryProjectionDefinition<EventType>;\n};\n\nexport const InMemoryProjectionSpec = {\n for: <EventType extends Event>(\n options: InMemoryProjectionSpecOptions<EventType>,\n ): InMemoryProjectionSpec<EventType> => {\n const { projection } = options;\n\n return (givenEvents: InMemoryProjectionSpecEvent<EventType>[]) => {\n return {\n when: (\n events: InMemoryProjectionSpecEvent<EventType>[],\n options?: InMemoryProjectionSpecWhenOptions,\n ) => {\n const allEvents: ReadEvent<EventType, InMemoryReadEventMetadata>[] =\n [];\n\n const run = async (database: InMemoryDatabase) => {\n let globalPosition = 0n;\n const numberOfTimes = options?.numberOfTimes ?? 1;\n\n for (const event of [\n ...givenEvents,\n ...Array.from({ length: numberOfTimes }).flatMap(() => events),\n ]) {\n const metadata: InMemoryReadEventMetadata = {\n globalPosition: ++globalPosition,\n streamPosition: globalPosition,\n streamName: event.metadata?.streamName ?? `test-${uuid()}`,\n messageId: uuid(),\n };\n\n allEvents.push({\n ...event,\n kind: 'Event',\n metadata: {\n ...metadata,\n ...('metadata' in event ? (event.metadata ?? {}) : {}),\n } as CombinedReadEventMetadata<\n EventType,\n InMemoryReadEventMetadata\n >,\n });\n }\n\n // Create a minimal mock EventStore implementation\n const mockEventStore = {\n database,\n aggregateStream: async () => {\n return Promise.resolve({\n state: {},\n currentStreamVersion: 0n,\n streamExists: false,\n });\n },\n readStream: async () => {\n return Promise.resolve({\n events: [],\n currentStreamVersion: 0n,\n streamExists: false,\n });\n },\n appendToStream: async () => {\n return Promise.resolve({\n nextExpectedStreamVersion: 0n,\n createdNewStream: false,\n });\n },\n } as InMemoryEventStore;\n\n await handleInMemoryProjections({\n events: allEvents,\n projections: [projection],\n database,\n eventStore: mockEventStore,\n });\n };\n\n return {\n then: async (\n assertFn: InMemoryProjectionAssert,\n message?: string,\n ): Promise<void> => {\n const database = getInMemoryDatabase();\n await run(database);\n\n const succeeded = await assertFn({ database });\n\n if (succeeded !== undefined && succeeded === false) {\n assertFails(\n message ??\n \"Projection specification didn't match the criteria\",\n );\n }\n },\n thenThrows: async <ErrorType extends Error = Error>(\n ...args: Parameters<ThenThrows<ErrorType>>\n ): Promise<void> => {\n const database = getInMemoryDatabase();\n try {\n await run(database);\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 }\n },\n };\n },\n };\n };\n },\n};\n\n// Helper functions for creating events in stream\nexport const eventInStream = <\n EventType extends Event = Event,\n EventMetaDataType extends\n InMemoryReadEventMetadata = InMemoryReadEventMetadata,\n>(\n streamName: string,\n event: InMemoryProjectionSpecEvent<EventType, EventMetaDataType>,\n): InMemoryProjectionSpecEvent<EventType, EventMetaDataType> => {\n return {\n ...event,\n metadata: {\n ...(event.metadata ?? {}),\n streamName: event.metadata?.streamName ?? streamName,\n } as Partial<EventMetaDataType>,\n };\n};\n\nexport const eventsInStream = <\n EventType extends Event = Event,\n EventMetaDataType extends\n InMemoryReadEventMetadata = InMemoryReadEventMetadata,\n>(\n streamName: string,\n events: InMemoryProjectionSpecEvent<EventType, EventMetaDataType>[],\n): InMemoryProjectionSpecEvent<EventType, EventMetaDataType>[] => {\n return events.map((e) => eventInStream(streamName, e));\n};\n\nexport const newEventsInStream = eventsInStream;\n\n// Assertion helpers for checking documents\nexport function documentExists<T extends DocumentWithId>(\n expected: Partial<T>,\n options: { inCollection: string; withId: string | number },\n): InMemoryProjectionAssert {\n return async ({ database }) => {\n const collection = database.collection<T>(options.inCollection);\n\n const document = await collection.findOne((doc) => {\n // Handle both string IDs and numeric IDs in a type-safe way\n const docId = '_id' in doc ? doc._id : undefined;\n return docId === options.withId;\n });\n\n if (!document) {\n assertFails(\n `Document with ID ${options.withId} does not exist in collection ${options.inCollection}`,\n );\n return Promise.resolve(false);\n }\n\n // Check that all expected properties exist with expected values\n for (const [key, value] of Object.entries(expected)) {\n const propKey = key as keyof typeof document;\n if (\n !(key in document) ||\n JSONParser.stringify(document[propKey]) !== JSONParser.stringify(value)\n ) {\n assertFails(`Property ${key} doesn't match the expected value`);\n return Promise.resolve(false);\n }\n }\n\n return Promise.resolve(true);\n };\n}\n\n// Helper for checking document contents\nexport const expectInMemoryDocuments = {\n fromCollection: <T extends DocumentWithId>(collectionName: string) => ({\n withId: (id: string | number) => ({\n toBeEqual: (expected: Partial<T>): InMemoryProjectionAssert =>\n documentExists<T>(expected, {\n inCollection: collectionName,\n withId: id,\n }),\n }),\n }),\n};\n","import { TransformStream } from 'web-streams-polyfill';\nimport type {\n Event,\n ReadEvent,\n ReadEventMetadataWithGlobalPosition,\n} from '../../typing';\nimport { globalStreamCaughtUp, type GlobalSubscriptionEvent } from '../events';\n\nexport const streamTrackingGlobalPosition = (\n currentEvents: ReadEvent<Event, ReadEventMetadataWithGlobalPosition>[],\n) => new CaughtUpTransformStream(currentEvents);\n\nexport class CaughtUpTransformStream extends TransformStream<\n ReadEvent<Event, ReadEventMetadataWithGlobalPosition>,\n | ReadEvent<Event, ReadEventMetadataWithGlobalPosition>\n | GlobalSubscriptionEvent\n> {\n private _currentPosition: bigint;\n private _logPosition: bigint;\n\n constructor(events: ReadEvent<Event, ReadEventMetadataWithGlobalPosition>[]) {\n super({\n start: (controller) => {\n let globalPosition = 0n;\n for (const event of events) {\n controller.enqueue(event);\n globalPosition = event.metadata.globalPosition;\n }\n controller.enqueue(globalStreamCaughtUp({ globalPosition }));\n },\n transform: (event, controller) => {\n this._currentPosition = event.metadata.globalPosition;\n controller.enqueue(event);\n\n if (this._currentPosition < this._logPosition) return;\n\n controller.enqueue(\n globalStreamCaughtUp({ globalPosition: this._currentPosition }),\n );\n },\n });\n\n this._currentPosition = this._logPosition =\n events.length > 0\n ? events[events.length - 1]!.metadata.globalPosition\n : 0n;\n }\n\n public set logPosition(value: bigint) {\n this._logPosition = value;\n }\n}\n","import { v4 as uuid } from 'uuid';\nimport { notifyAboutNoActiveReadersStream } from '../../streaming/transformations/notifyAboutNoActiveReaders';\nimport { writeToStream } from '../../streaming/writers';\nimport type {\n Event,\n ReadEvent,\n ReadEventMetadataWithGlobalPosition,\n} from '../../typing';\nimport {\n CaughtUpTransformStream,\n streamTrackingGlobalPosition,\n} from './caughtUpTransformStream';\n\nexport const StreamingCoordinator = () => {\n const allEvents: ReadEvent<Event, ReadEventMetadataWithGlobalPosition>[] = [];\n const listeners = new Map<string, CaughtUpTransformStream>();\n\n return {\n notify: async (\n events: ReadEvent<Event, ReadEventMetadataWithGlobalPosition>[],\n ) => {\n if (events.length === 0) return;\n\n allEvents.push(...events);\n\n for (const listener of listeners.values()) {\n listener.logPosition =\n events[events.length - 1]!.metadata.globalPosition;\n\n await writeToStream(listener, events);\n }\n },\n\n stream: () => {\n const streamId = uuid();\n const transformStream = streamTrackingGlobalPosition(allEvents);\n\n listeners.set(streamId, transformStream);\n return transformStream.readable.pipeThrough(\n notifyAboutNoActiveReadersStream(\n (stream) => {\n if (listeners.has(stream.streamId))\n listeners.delete(stream.streamId);\n },\n { streamId },\n ),\n );\n },\n };\n};\n","import { v4 as uuid } from 'uuid';\nimport { TransformStream } from 'web-streams-polyfill';\n\nexport const notifyAboutNoActiveReadersStream = <Item>(\n onNoActiveReaderCallback: (\n stream: NotifyAboutNoActiveReadersStream<Item>,\n ) => void,\n options: { streamId?: string; intervalCheckInMs?: number } = {},\n) => new NotifyAboutNoActiveReadersStream(onNoActiveReaderCallback, options);\n\nexport class NotifyAboutNoActiveReadersStream<Item> extends TransformStream<\n Item,\n Item\n> {\n private checkInterval: NodeJS.Timeout | null = null;\n public readonly streamId: string;\n private _isStopped: boolean = false;\n public get hasActiveSubscribers() {\n return !this._isStopped;\n }\n\n constructor(\n private onNoActiveReaderCallback: (\n stream: NotifyAboutNoActiveReadersStream<Item>,\n ) => void,\n options: { streamId?: string; intervalCheckInMs?: number } = {},\n ) {\n super({\n cancel: (reason) => {\n console.log('Stream was canceled. Reason:', reason);\n this.stopChecking();\n },\n });\n this.streamId = options?.streamId ?? uuid();\n\n this.onNoActiveReaderCallback = onNoActiveReaderCallback;\n\n this.startChecking(options?.intervalCheckInMs ?? 20);\n }\n\n private startChecking(interval: number) {\n this.checkInterval = setInterval(() => {\n this.checkNoActiveReader();\n }, interval);\n }\n\n private stopChecking() {\n if (!this.checkInterval) return;\n\n clearInterval(this.checkInterval);\n this.checkInterval = null;\n this._isStopped = true;\n this.onNoActiveReaderCallback(this);\n }\n\n private checkNoActiveReader() {\n if (!this.readable.locked && !this._isStopped) {\n this.stopChecking();\n }\n }\n}\n","import { ReadableStream } from 'web-streams-polyfill';\n\nexport const fromArray = <T>(chunks: T[]) =>\n new ReadableStream<T>({\n start(controller) {\n for (const chunk of chunks) controller.enqueue(chunk);\n controller.close();\n },\n });\n","import {\n type ReadableStream,\n type ReadableStreamDefaultReadResult,\n type TransformStreamDefaultController,\n} from 'web-streams-polyfill';\nimport type { AsyncRetryOptions } from '../utils';\nimport type { Decoder } from './decoders';\nimport { DefaultDecoder } from './decoders/composite';\nimport { streamTransformations } from './transformations';\n\nconst { retry } = streamTransformations;\n\nexport const restream = <\n Source = unknown,\n Transformed = Source,\n StreamType = Source,\n>(\n createSourceStream: () => ReadableStream<StreamType>,\n transform: (input: Source) => Transformed = (source) =>\n source as unknown as Transformed,\n retryOptions: AsyncRetryOptions = { forever: true, minTimeout: 25 },\n decoder: Decoder<StreamType, Source> = new DefaultDecoder<Source>(),\n): ReadableStream<Transformed> =>\n retry(createSourceStream, handleChunk(transform, decoder), retryOptions)\n .readable;\n\nconst handleChunk =\n <Source = unknown, Transformed = Source, StreamType = Source>(\n transform: (input: Source) => Transformed = (source) =>\n source as unknown as Transformed,\n decoder: Decoder<StreamType, Source> = new DefaultDecoder<Source>(),\n ) =>\n (\n readResult: ReadableStreamDefaultReadResult<StreamType>,\n controller: TransformStreamDefaultController<Transformed>,\n ): void => {\n const { done: isDone, value } = readResult;\n\n if (value) decoder.addToBuffer(value);\n\n if (!isDone && !decoder.hasCompleteMessage()) return;\n\n decodeAndTransform(decoder, transform, controller);\n };\n\nconst decodeAndTransform = <StreamType, Source, Transformed = Source>(\n decoder: Decoder<StreamType, Source>,\n transform: (input: Source) => Transformed,\n controller: TransformStreamDefaultController<Transformed>,\n) => {\n try {\n const decoded = decoder.decode();\n if (!decoded) return; // TODO: Add a proper handling of decode errors\n\n const transformed = transform(decoded);\n controller.enqueue(transformed);\n } catch (error) {\n controller.error(new Error(`Decoding error: ${error?.toString()}`));\n }\n};\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const filter = <Item>(filter: (item: Item) => boolean) =>\n new TransformStream<Item, Item>({\n transform(chunk, controller) {\n if (filter(chunk)) {\n controller.enqueue(chunk);\n }\n },\n });\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const map = <From, To>(map: (item: From) => To) =>\n new TransformStream<From, To>({\n transform(chunk, controller) {\n controller.enqueue(map(chunk));\n },\n });\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const reduce = <I, O>(\n reducer: (accumulator: O, chunk: I) => O,\n initialValue: O,\n) => new ReduceTransformStream<I, O>(reducer, initialValue);\n\nexport class ReduceTransformStream<I, O> extends TransformStream<I, O> {\n private accumulator: O;\n private reducer: (accumulator: O, chunk: I) => O;\n\n constructor(reducer: (accumulator: O, chunk: I) => O, initialValue: O) {\n super({\n transform: (chunk) => {\n this.accumulator = this.reducer(this.accumulator, chunk);\n },\n flush: (controller) => {\n controller.enqueue(this.accumulator);\n controller.terminate();\n },\n });\n\n this.accumulator = initialValue;\n this.reducer = reducer;\n }\n}\n","import {\n type ReadableStream,\n type ReadableStreamDefaultReadResult,\n TransformStream,\n type TransformStreamDefaultController,\n} from 'web-streams-polyfill';\nimport { type AsyncRetryOptions, asyncRetry } from '../../utils';\n\nexport const retryStream = <\n Source = unknown,\n Transformed = Source,\n StreamType = Source,\n>(\n createSourceStream: () => ReadableStream<StreamType>,\n handleChunk: (\n readResult: ReadableStreamDefaultReadResult<StreamType>,\n controller: TransformStreamDefaultController<Transformed>,\n ) => Promise<void> | void,\n retryOptions: AsyncRetryOptions = { forever: true, minTimeout: 25 },\n): TransformStream<Source, Transformed> =>\n new TransformStream<Source, Transformed>({\n start(controller) {\n asyncRetry(\n () => onRestream(createSourceStream, handleChunk, controller),\n retryOptions,\n ).catch((error) => {\n controller.error(error);\n });\n },\n });\n\nconst onRestream = async <StreamType, Source, Transformed = Source>(\n createSourceStream: () => ReadableStream<StreamType>,\n handleChunk: (\n readResult: ReadableStreamDefaultReadResult<StreamType>,\n controller: TransformStreamDefaultController<Transformed>,\n ) => Promise<void> | void,\n controller: TransformStreamDefaultController<Transformed>,\n): Promise<void> => {\n const sourceStream = createSourceStream();\n const reader = sourceStream.getReader();\n\n try {\n let done: boolean;\n\n do {\n const result = await reader.read();\n done = result.done;\n\n await handleChunk(result, controller);\n\n if (done) {\n controller.terminate();\n }\n } while (!done);\n } finally {\n reader.releaseLock();\n }\n};\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const skip = <T>(limit: number) => new SkipTransformStream<T>(limit);\n\nexport class SkipTransformStream<T> extends TransformStream<T, T> {\n private count = 0;\n private skip: number;\n\n constructor(skip: number) {\n super({\n transform: (chunk, controller) => {\n this.count++;\n if (this.count > this.skip) {\n controller.enqueue(chunk);\n }\n },\n });\n\n this.skip = skip;\n }\n}\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const stopAfter = <Item>(stopCondition: (item: Item) => boolean) =>\n new TransformStream<Item, Item>({\n transform(chunk, controller) {\n controller.enqueue(chunk);\n\n if (stopCondition(chunk)) {\n controller.terminate();\n }\n },\n });\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const stopOn = <Item>(stopCondition: (item: Item) => boolean) =>\n new TransformStream<Item, Item>({\n async transform(chunk, controller) {\n if (!stopCondition(chunk)) {\n controller.enqueue(chunk);\n return;\n }\n await Promise.resolve();\n controller.terminate();\n },\n });\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const take = <T>(limit: number) => new TakeTransformStream<T>(limit);\n\nexport class TakeTransformStream<T> extends TransformStream<T, T> {\n private count = 0;\n private limit: number;\n\n constructor(limit: number) {\n super({\n transform: (chunk, controller) => {\n if (this.count < this.limit) {\n this.count++;\n controller.enqueue(chunk);\n } else {\n controller.terminate();\n }\n },\n });\n\n this.limit = limit;\n }\n}\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const waitAtMost = <Item>(waitTimeInMs: number) =>\n new TransformStream<Item, Item>({\n start(controller) {\n const timeoutId = setTimeout(() => {\n controller.terminate();\n }, waitTimeInMs);\n\n const originalTerminate = controller.terminate.bind(controller);\n\n // Clear the timeout if the stream is terminated early\n controller.terminate = () => {\n clearTimeout(timeoutId);\n originalTerminate();\n };\n },\n transform(chunk, controller) {\n controller.enqueue(chunk);\n },\n });\n","import { ConcurrencyError } from '../errors';\nimport type { BigIntStreamPosition, Flavour } from '../typing';\n\nexport type ExpectedStreamVersion<VersionType = BigIntStreamPosition> =\n | ExpectedStreamVersionWithValue<VersionType>\n | ExpectedStreamVersionGeneral;\n\nexport type ExpectedStreamVersionWithValue<VersionType = BigIntStreamPosition> =\n Flavour<VersionType, 'StreamVersion'>;\n\nexport type ExpectedStreamVersionGeneral = Flavour<\n 'STREAM_EXISTS' | 'STREAM_DOES_NOT_EXIST' | 'NO_CONCURRENCY_CHECK',\n 'StreamVersion'\n>;\n\nexport const STREAM_EXISTS = 'STREAM_EXISTS' as ExpectedStreamVersionGeneral;\nexport const STREAM_DOES_NOT_EXIST =\n 'STREAM_DOES_NOT_EXIST' as ExpectedStreamVersionGeneral;\nexport const NO_CONCURRENCY_CHECK =\n 'NO_CONCURRENCY_CHECK' as ExpectedStreamVersionGeneral;\n\nexport const matchesExpectedVersion = <StreamVersion = BigIntStreamPosition>(\n current: StreamVersion | undefined,\n expected: ExpectedStreamVersion<StreamVersion>,\n defaultVersion: StreamVersion,\n): boolean => {\n if (expected === NO_CONCURRENCY_CHECK) return true;\n\n if (expected == STREAM_DOES_NOT_EXIST) return current === defaultVersion;\n\n if (expected == STREAM_EXISTS) return current !== defaultVersion;\n\n return current === expected;\n};\n\nexport const assertExpectedVersionMatchesCurrent = <\n StreamVersion = BigIntStreamPosition,\n>(\n current: StreamVersion,\n expected: ExpectedStreamVersion<StreamVersion> | undefined,\n defaultVersion: StreamVersion,\n): void => {\n expected ??= NO_CONCURRENCY_CHECK;\n\n if (!matchesExpectedVersion(current, expected, defaultVersion))\n throw new ExpectedVersionConflictError(current, expected);\n};\n\nexport class ExpectedVersionConflictError<\n VersionType = BigIntStreamPosition,\n> extends ConcurrencyError {\n constructor(\n current: VersionType,\n expected: ExpectedStreamVersion<VersionType>,\n ) {\n super(current?.toString(), expected?.toString());\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, ExpectedVersionConflictError.prototype);\n }\n}\n\nexport const isExpectedVersionConflictError = (\n error: unknown,\n): error is ExpectedVersionConflictError =>\n error instanceof ExpectedVersionConflictError;\n","const isPrimitive = (value: unknown): boolean => {\n const type = typeof value;\n return (\n value === null ||\n value === undefined ||\n type === 'boolean' ||\n type === 'number' ||\n type === 'string' ||\n type === 'symbol' ||\n type === 'bigint'\n );\n};\n\nconst compareArrays = <T>(left: T[], right: T[]): boolean => {\n if (left.length !== right.length) {\n return false;\n }\n for (let i = 0; i < left.length; i++) {\n const leftHas = i in left;\n const rightHas = i in right;\n if (leftHas !== rightHas) return false;\n if (leftHas && !deepEquals(left[i], right[i])) return false;\n }\n return true;\n};\n\nconst compareDates = (left: Date, right: Date): boolean => {\n return left.getTime() === right.getTime();\n};\n\nconst compareRegExps = (left: RegExp, right: RegExp): boolean => {\n return left.toString() === right.toString();\n};\n\nconst compareErrors = (left: Error, right: Error): boolean => {\n if (left.message !== right.message || left.name !== right.name) {\n return false;\n }\n const leftKeys = Object.keys(left);\n const rightKeys = Object.keys(right);\n if (leftKeys.length !== rightKeys.length) return false;\n const rightKeySet = new Set(rightKeys);\n for (const key of leftKeys) {\n if (!rightKeySet.has(key)) return false;\n // @ts-expect-error - accessing dynamic keys\n if (!deepEquals(left[key], right[key])) return false;\n }\n return true;\n};\n\nconst compareMaps = (\n left: Map<unknown, unknown>,\n right: Map<unknown, unknown>,\n): boolean => {\n if (left.size !== right.size) return false;\n\n for (const [key, value] of left) {\n if (isPrimitive(key)) {\n if (!right.has(key) || !deepEquals(value, right.get(key))) {\n return false;\n }\n } else {\n let found = false;\n for (const [rightKey, rightValue] of right) {\n if (deepEquals(key, rightKey) && deepEquals(value, rightValue)) {\n found = true;\n break;\n }\n }\n if (!found) return false;\n }\n }\n return true;\n};\n\nconst compareSets = (left: Set<unknown>, right: Set<unknown>): boolean => {\n if (left.size !== right.size) return false;\n\n for (const leftItem of left) {\n if (isPrimitive(leftItem)) {\n if (!right.has(leftItem)) return false;\n } else {\n let found = false;\n for (const rightItem of right) {\n if (deepEquals(leftItem, rightItem)) {\n found = true;\n break;\n }\n }\n if (!found) return false;\n }\n }\n return true;\n};\n\nconst compareArrayBuffers = (\n left: ArrayBuffer,\n right: ArrayBuffer,\n): boolean => {\n if (left.byteLength !== right.byteLength) return false;\n const leftView = new Uint8Array(left);\n const rightView = new Uint8Array(right);\n for (let i = 0; i < leftView.length; i++) {\n if (leftView[i] !== rightView[i]) return false;\n }\n return true;\n};\n\nconst compareTypedArrays = (\n left: ArrayBufferView,\n right: ArrayBufferView,\n): boolean => {\n if (left.constructor !== right.constructor) return false;\n if (left.byteLength !== right.byteLength) return false;\n\n const leftArray = new Uint8Array(\n left.buffer,\n left.byteOffset,\n left.byteLength,\n );\n const rightArray = new Uint8Array(\n right.buffer,\n right.byteOffset,\n right.byteLength,\n );\n\n for (let i = 0; i < leftArray.length; i++) {\n if (leftArray[i] !== rightArray[i]) return false;\n }\n return true;\n};\n\nconst compareObjects = (\n left: Record<string, unknown>,\n right: Record<string, unknown>,\n): boolean => {\n const keys1 = Object.keys(left);\n const keys2 = Object.keys(right);\n\n if (keys1.length !== keys2.length) {\n return false;\n }\n\n for (const key of keys1) {\n if (left[key] instanceof Function && right[key] instanceof Function) {\n continue;\n }\n\n const isEqual = deepEquals(left[key], right[key]);\n if (!isEqual) {\n return false;\n }\n }\n\n return true;\n};\n\nconst getType = (value: unknown): string => {\n if (value === null) return 'null';\n if (value === undefined) return 'undefined';\n\n const primitiveType = typeof value;\n if (primitiveType !== 'object') return primitiveType;\n\n if (Array.isArray(value)) return 'array';\n if (value instanceof Boolean) return 'boxed-boolean';\n if (value instanceof Number) return 'boxed-number';\n if (value instanceof String) return 'boxed-string';\n if (value instanceof Date) return 'date';\n if (value instanceof RegExp) return 'regexp';\n if (value instanceof Error) return 'error';\n if (value instanceof Map) return 'map';\n if (value instanceof Set) return 'set';\n if (value instanceof ArrayBuffer) return 'arraybuffer';\n if (value instanceof DataView) return 'dataview';\n if (value instanceof WeakMap) return 'weakmap';\n if (value instanceof WeakSet) return 'weakset';\n\n if (ArrayBuffer.isView(value)) return 'typedarray';\n\n return 'object';\n};\n\nexport const deepEquals = <T>(left: T, right: T): boolean => {\n if (left === right) return true;\n\n if (isEquatable(left)) {\n return left.equals(right);\n }\n\n const leftType = getType(left);\n const rightType = getType(right);\n\n if (leftType !== rightType) return false;\n\n switch (leftType) {\n case 'null':\n case 'undefined':\n case 'boolean':\n case 'number':\n case 'bigint':\n case 'string':\n case 'symbol':\n case 'function':\n return left === right;\n\n case 'array':\n return compareArrays(left as unknown[], right as unknown[]);\n\n case 'date':\n return compareDates(left as Date, right as Date);\n\n case 'regexp':\n return compareRegExps(left as RegExp, right as RegExp);\n\n case 'error':\n return compareErrors(left as Error, right as Error);\n\n case 'map':\n return compareMaps(\n left as Map<unknown, unknown>,\n right as Map<unknown, unknown>,\n );\n\n case 'set':\n return compareSets(left as Set<unknown>, right as Set<unknown>);\n\n case 'arraybuffer':\n return compareArrayBuffers(left as ArrayBuffer, right as ArrayBuffer);\n\n case 'dataview':\n case 'weakmap':\n case 'weakset':\n return false;\n\n case 'typedarray':\n return compareTypedArrays(\n left as ArrayBufferView,\n right as ArrayBufferView,\n );\n\n case 'boxed-boolean':\n return (left as boolean).valueOf() === (right as boolean).valueOf();\n\n case 'boxed-number':\n return (left as number).valueOf() === (right as number).valueOf();\n\n case 'boxed-string':\n return (left as string).valueOf() === (right as string).valueOf();\n\n case 'object':\n return compareObjects(\n left as Record<string, unknown>,\n right as Record<string, unknown>,\n );\n\n default:\n return false;\n }\n};\n\nexport type Equatable<T> = { equals: (right: T) => boolean } & T;\n\nexport const isEquatable = <T>(left: T): left is Equatable<T> => {\n return (\n left !== null &&\n left !== undefined &&\n typeof left === 'object' &&\n 'equals' in left &&\n typeof left['equals'] === 'function'\n );\n};\n","export class ParseError extends Error {\n constructor(text: string) {\n super(`Cannot parse! ${text}`);\n }\n}\n\nexport type Mapper<From, To = From> =\n | ((value: unknown) => To)\n | ((value: Partial<From>) => To)\n | ((value: From) => To)\n | ((value: Partial<To>) => To)\n | ((value: To) => To)\n | ((value: Partial<To | From>) => To)\n | ((value: To | From) => To);\n\nexport type MapperArgs<From, To = From> = Partial<From> &\n From &\n Partial<To> &\n To;\n\nexport type ParseOptions<From, To = From> = {\n reviver?: (key: string, value: unknown) => unknown;\n map?: Mapper<From, To>;\n typeCheck?: <To>(value: unknown) => value is To;\n};\n\nexport type StringifyOptions<From, To = From> = {\n map?: Mapper<From, To>;\n};\n\nexport const JSONParser = {\n stringify: <From, To = From>(\n value: From,\n options?: StringifyOptions<From, To>,\n ) => {\n return JSON.stringify(\n options?.map ? options.map(value as MapperArgs<From, To>) : value,\n //TODO: Consider adding support to DateTime and adding specific format to mark that's a bigint\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n (_, v) => (typeof v === 'bigint' ? v.toString() : v),\n );\n },\n parse: <From, To = From>(\n text: string,\n options?: ParseOptions<From, To>,\n ): To | undefined => {\n const parsed: unknown = JSON.parse(text, options?.reviver);\n\n if (options?.typeCheck && !options?.typeCheck<To>(parsed))\n throw new ParseError(text);\n\n return options?.map\n ? options.map(parsed as MapperArgs<From, To>)\n : (parsed as To | undefined);\n },\n};\n","import { JSONParser } from '../serialization';\nimport type { DefaultRecord } from '../typing';\nimport { deepEquals } from '../utils';\n\nexport class AssertionError extends Error {\n constructor(message: string) {\n super(message);\n }\n}\n\nexport const isSubset = (superObj: unknown, subObj: unknown): boolean => {\n const sup = superObj as DefaultRecord;\n const sub = subObj as DefaultRecord;\n\n assertOk(sup);\n assertOk(sub);\n\n return Object.keys(sub).every((ele: string) => {\n if (typeof sub[ele] == 'object') {\n return isSubset(sup[ele], sub[ele]);\n }\n return sub[ele] === sup[ele];\n });\n};\n\nexport const assertFails = (message?: string) => {\n throw new AssertionError(message ?? 'That should not ever happened, right?');\n};\n\nexport const assertThrowsAsync = async <TError extends Error>(\n fun: () => Promise<void>,\n errorCheck?: (error: Error) => boolean,\n): Promise<TError> => {\n try {\n await fun();\n } catch (error) {\n const typedError = error as TError;\n if (typedError instanceof AssertionError || !errorCheck) {\n assertFalse(\n typedError instanceof AssertionError,\n \"Function didn't throw expected error\",\n );\n return typedError;\n }\n\n assertTrue(\n errorCheck(typedError),\n `Error doesn't match the expected condition: ${JSONParser.stringify(error)}`,\n );\n\n return typedError;\n }\n throw new AssertionError(\"Function didn't throw expected error\");\n};\n\nexport const assertThrows = <TError extends Error>(\n fun: () => void,\n errorCheck?: (error: Error) => boolean,\n): TError => {\n try {\n fun();\n } catch (error) {\n const typedError = error as TError;\n\n if (errorCheck) {\n assertTrue(\n errorCheck(typedError),\n `Error doesn't match the expected condition: ${JSONParser.stringify(error)}`,\n );\n } else if (typedError instanceof AssertionError) {\n assertFalse(\n typedError instanceof AssertionError,\n \"Function didn't throw expected error\",\n );\n }\n\n return typedError;\n }\n throw new AssertionError(\"Function didn't throw expected error\");\n};\n\nexport const assertDoesNotThrow = <TError extends Error>(\n fun: () => void,\n errorCheck?: (error: Error) => boolean,\n): TError | null => {\n try {\n fun();\n return null;\n } catch (error) {\n const typedError = error as TError;\n\n if (errorCheck) {\n assertFalse(\n errorCheck(typedError),\n `Error matching the expected condition was thrown!: ${JSONParser.stringify(error)}`,\n );\n } else {\n assertFails(`Function threw an error: ${JSONParser.stringify(error)}`);\n }\n\n return typedError;\n }\n};\n\nexport const assertRejects = async <T, TError extends Error = Error>(\n promise: Promise<T>,\n errorCheck?: ((error: TError) => boolean) | TError,\n) => {\n try {\n await promise;\n throw new AssertionError(\"Function didn't throw expected error\");\n } catch (error) {\n if (!errorCheck) return;\n\n if (errorCheck instanceof Error) assertDeepEqual(error, errorCheck);\n else assertTrue(errorCheck(error as TError));\n }\n};\n\nexport const assertMatches = (\n actual: unknown,\n expected: unknown,\n message?: string,\n) => {\n if (!isSubset(actual, expected))\n throw new AssertionError(\n message ??\n `subObj:\\n${JSONParser.stringify(expected)}\\nis not subset of\\n${JSONParser.stringify(actual)}`,\n );\n};\n\nexport const assertDeepEqual = <T = unknown>(\n actual: T,\n expected: T,\n message?: string,\n) => {\n if (!deepEquals(actual, expected))\n throw new AssertionError(\n message ??\n `subObj:\\n${JSONParser.stringify(expected)}\\nis not equal to\\n${JSONParser.stringify(actual)}`,\n );\n};\n\nexport const assertNotDeepEqual = <T = unknown>(\n actual: T,\n expected: T,\n message?: string,\n) => {\n if (deepEquals(actual, expected))\n throw new AssertionError(\n message ??\n `subObj:\\n${JSONParser.stringify(expected)}\\nis equals to\\n${JSONParser.stringify(actual)}`,\n );\n};\n\nexport const assertThat = <T>(item: T) => {\n return {\n isEqualTo: (other: T) => assertTrue(deepEquals(item, other)),\n };\n};\n\nexport function assertFalse(\n condition: boolean,\n message?: string,\n): asserts condition is false {\n if (condition !== false)\n throw new AssertionError(message ?? `Condition is true`);\n}\n\nexport function assertTrue(\n condition: boolean,\n message?: string,\n): asserts condition is true {\n if (condition !== true)\n throw new AssertionError(message ?? `Condition is false`);\n}\n\nexport function assertOk<T>(\n obj: T | null | undefined,\n message?: string,\n): asserts obj is T {\n if (!obj) throw new AssertionError(message ?? `Condition is not truthy`);\n}\n\nexport function assertEqual<T>(\n expected: T | null | undefined,\n actual: T | null | undefined,\n message?: string,\n): void {\n if (expected !== actual)\n throw new AssertionError(\n `${message ?? 'Objects are not equal'}:\\nExpected: ${JSONParser.stringify(expected)}\\nActual: ${JSONParser.stringify(actual)}`,\n );\n}\n\nexport function assertNotEqual<T>(\n obj: T | null | undefined,\n other: T | null | undefined,\n message?: string,\n): void {\n if (obj === other)\n throw new AssertionError(\n message ?? `Objects are equal: ${JSONParser.stringify(obj)}`,\n );\n}\n\nexport function assertIsNotNull<T extends object | bigint>(\n result: T | null,\n): asserts result is T {\n assertNotEqual(result, null);\n assertOk(result);\n}\n\nexport function assertIsNull<T extends object>(\n result: T | null,\n): asserts result is null {\n assertEqual(result, null);\n}\n\ntype Call = {\n arguments: unknown[];\n result: unknown;\n target: unknown;\n this: unknown;\n};\n\nexport type ArgumentMatcher = (arg: unknown) => boolean;\n\nexport const argValue =\n <T>(value: T): ArgumentMatcher =>\n (arg) =>\n deepEquals(arg, value);\n\nexport const argMatches =\n <T>(matches: (arg: T) => boolean): ArgumentMatcher =>\n (arg) =>\n matches(arg as T);\n\n// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\nexport type MockedFunction = Function & { mock?: { calls: Call[] } };\n\nexport function verifyThat(fn: MockedFunction) {\n return {\n calledTimes: (times: number) => {\n assertEqual(fn.mock?.calls?.length, times);\n },\n notCalled: () => {\n assertEqual(fn?.mock?.calls?.length, 0);\n },\n called: () => {\n assertTrue(\n fn.mock?.calls.length !== undefined && fn.mock.calls.length > 0,\n );\n },\n calledWith: (...args: unknown[]) => {\n assertTrue(\n fn.mock?.calls.length !== undefined &&\n fn.mock.calls.length >= 1 &&\n fn.mock.calls.some((call) => deepEquals(call.arguments, args)),\n );\n },\n calledOnceWith: (...args: unknown[]) => {\n assertTrue(\n fn.mock?.calls.length !== undefined &&\n fn.mock.calls.length === 1 &&\n fn.mock.calls.some((call) => deepEquals(call.arguments, args)),\n );\n },\n calledWithArgumentMatching: (...matches: ArgumentMatcher[]) => {\n assertTrue(\n fn.mock?.calls.length !== undefined && fn.mock.calls.length >= 1,\n );\n assertTrue(\n fn.mock?.calls.length !== undefined &&\n fn.mock.calls.length >= 1 &&\n fn.mock.calls.some(\n (call) =>\n call.arguments &&\n call.arguments.length >= matches.length &&\n matches.every((match, index) => match(call.arguments[index])),\n ),\n );\n },\n notCalledWithArgumentMatching: (...matches: ArgumentMatcher[]) => {\n assertFalse(\n fn.mock?.calls.length !== undefined &&\n fn.mock.calls.length >= 1 &&\n fn.mock.calls[0]!.arguments &&\n fn.mock.calls[0]!.arguments.length >= matches.length &&\n matches.every((match, index) =>\n match(fn.mock!.calls[0]!.arguments[index]),\n ),\n );\n },\n };\n}\n\nexport const assertThatArray = <T>(array: T[]) => {\n return {\n isEmpty: () =>\n assertEqual(\n array.length,\n 0,\n `Array is not empty ${JSONParser.stringify(array)}`,\n ),\n isNotEmpty: () => assertNotEqual(array.length, 0, `Array is empty`),\n hasSize: (length: number) => assertEqual(array.length, length),\n containsElements: (other: T[]) => {\n assertTrue(other.every((ts) => array.some((o) => deepEquals(ts, o))));\n },\n containsElementsMatching: (other: T[]) => {\n assertTrue(other.every((ts) => array.some((o) => isSubset(o, ts))));\n },\n containsOnlyElementsMatching: (other: T[]) => {\n assertEqual(array.length, other.length, `Arrays lengths don't match`);\n assertTrue(other.every((ts) => array.some((o) => isSubset(o, ts))));\n },\n containsExactlyInAnyOrder: (other: T[]) => {\n assertEqual(array.length, other.length);\n assertTrue(array.every((ts) => other.some((o) => deepEquals(ts, o))));\n },\n containsExactlyInAnyOrderElementsOf: (other: T[]) => {\n assertEqual(array.length, other.length);\n assertTrue(array.every((ts) => other.some((o) => deepEquals(ts, o))));\n },\n containsExactlyElementsOf: (other: T[]) => {\n assertEqual(array.length, other.length);\n for (let i = 0; i < array.length; i++) {\n assertTrue(deepEquals(array[i], other[i]));\n }\n },\n containsExactly: (elem: T) => {\n assertEqual(array.length, 1);\n assertTrue(deepEquals(array[0], elem));\n },\n contains: (elem: T) => {\n assertTrue(array.some((a) => deepEquals(a, elem)));\n },\n containsOnlyOnceElementsOf: (other: T[]) => {\n assertTrue(\n other\n .map((o) => array.filter((a) => deepEquals(a, o)).length)\n .filter((a) => a === 1).length === other.length,\n );\n },\n containsAnyOf: (other: T[]) => {\n assertTrue(array.some((a) => other.some((o) => deepEquals(a, o))));\n },\n allMatch: (matches: (item: T) => boolean) => {\n assertTrue(array.every(matches));\n },\n anyMatches: (matches: (item: T) => boolean) => {\n assertTrue(array.some(matches));\n },\n allMatchAsync: async (\n matches: (item: T) => Promise<boolean>,\n ): Promise<void> => {\n for (const item of array) {\n assertTrue(await matches(item));\n }\n },\n };\n};\n","import type { EmmettError } from '../errors';\nimport type { ProjectionDefinition } from '../projections';\nimport {\n type AnyEvent,\n type AnyMessage,\n type AnyReadEventMetadata,\n type AnyRecordedMessageMetadata,\n type BatchRecordedMessageHandlerWithContext,\n type CanHandle,\n type DefaultRecord,\n type Event,\n type GlobalPositionTypeOfRecordedMessageMetadata,\n type Message,\n type MessageHandlerResult,\n type RecordedMessage,\n type SingleMessageHandlerWithContext,\n type SingleRecordedMessageHandlerWithContext,\n} from '../typing';\nimport { isBigint } from '../validation';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type CurrentMessageProcessorPosition<CheckpointType = any> =\n | { lastCheckpoint: CheckpointType }\n | 'BEGINNING'\n | 'END';\n\nexport type GetCheckpoint<\n MessageType extends AnyMessage = AnyMessage,\n MessageMetadataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n CheckpointType = GlobalPositionTypeOfRecordedMessageMetadata<MessageMetadataType>,\n> = (\n message: RecordedMessage<MessageType, MessageMetadataType>,\n) => CheckpointType | null;\n\nexport const getCheckpoint = <\n MessageType extends AnyMessage = AnyMessage,\n MessageMetadataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n CheckpointType = GlobalPositionTypeOfRecordedMessageMetadata<MessageMetadataType>,\n>(\n message: RecordedMessage<MessageType, MessageMetadataType>,\n): CheckpointType | null => {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return 'checkpoint' in message.metadata &&\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n isBigint(message.metadata.checkpoint)\n ? // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n message.metadata.checkpoint\n : 'globalPosition' in message.metadata &&\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n isBigint(message.metadata.globalPosition)\n ? // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n message.metadata.globalPosition\n : 'streamPosition' in message.metadata &&\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n isBigint(message.metadata.streamPosition)\n ? // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n message.metadata.streamPosition\n : null;\n};\n\nexport const wasMessageHandled = <\n MessageType extends AnyMessage = AnyMessage,\n MessageMetadataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n CheckpointType = GlobalPositionTypeOfRecordedMessageMetadata<MessageMetadataType>,\n>(\n message: RecordedMessage<MessageType, MessageMetadataType>,\n checkpoint: CheckpointType | null,\n): boolean => {\n //TODO Make it smarter\n const messageCheckpoint = getCheckpoint(message);\n const checkpointBigint = checkpoint as bigint | null;\n\n return (\n messageCheckpoint !== null &&\n messageCheckpoint !== undefined &&\n checkpointBigint !== null &&\n checkpointBigint !== undefined &&\n messageCheckpoint <= checkpointBigint\n );\n};\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type MessageProcessorStartFrom<CheckpointType = any> =\n | CurrentMessageProcessorPosition<CheckpointType>\n | 'CURRENT';\n\nexport type MessageProcessorType = 'projector' | 'reactor';\nexport const MessageProcessorType = {\n PROJECTOR: 'projector' as MessageProcessorType,\n REACTOR: 'reactor' as MessageProcessorType,\n};\n\nexport type MessageProcessor<\n MessageType extends AnyMessage = AnyMessage,\n MessageMetadataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n HandlerContext extends DefaultRecord | undefined = undefined,\n CheckpointType = GlobalPositionTypeOfRecordedMessageMetadata<MessageMetadataType>,\n> = {\n id: string;\n type: string;\n start: (\n options: Partial<HandlerContext>,\n ) => Promise<CurrentMessageProcessorPosition<CheckpointType> | undefined>;\n close: () => Promise<void>;\n isActive: boolean;\n handle: BatchRecordedMessageHandlerWithContext<\n MessageType,\n MessageMetadataType,\n Partial<HandlerContext>\n >;\n};\n\nexport const MessageProcessor = {\n result: {\n skip: (options?: { reason?: string }): MessageHandlerResult => ({\n type: 'SKIP',\n ...(options ?? {}),\n }),\n stop: (options?: {\n reason?: string;\n error?: EmmettError;\n }): MessageHandlerResult => ({\n type: 'STOP',\n ...(options ?? {}),\n }),\n },\n};\n\nexport type MessageProcessingScope<\n HandlerContext extends DefaultRecord | undefined = undefined,\n> = <Result = MessageHandlerResult>(\n handler: (context: HandlerContext) => Result | Promise<Result>,\n partialContext: Partial<HandlerContext>,\n) => Result | Promise<Result>;\n\nexport type Checkpointer<\n MessageType extends AnyMessage = AnyMessage,\n MessageMetadataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n HandlerContext extends DefaultRecord = DefaultRecord,\n CheckpointType = GlobalPositionTypeOfRecordedMessageMetadata<MessageMetadataType>,\n> = {\n read: ReadProcessorCheckpoint<CheckpointType, HandlerContext>;\n store: StoreProcessorCheckpoint<\n MessageType,\n MessageMetadataType,\n CheckpointType,\n HandlerContext\n >;\n};\n\nexport type BaseMessageProcessorOptions<\n MessageType extends AnyMessage = AnyMessage,\n MessageMetadataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n HandlerContext extends DefaultRecord = DefaultRecord,\n CheckpointType = GlobalPositionTypeOfRecordedMessageMetadata<MessageMetadataType>,\n> = {\n type?: string;\n processorId: string;\n version?: number;\n partition?: string;\n startFrom?: MessageProcessorStartFrom<CheckpointType>;\n stopAfter?: (\n message: RecordedMessage<MessageType, MessageMetadataType>,\n ) => boolean;\n processingScope?: MessageProcessingScope<HandlerContext>;\n checkpoints?: Checkpointer<\n MessageType,\n MessageMetadataType,\n HandlerContext,\n CheckpointType\n >;\n canHandle?: CanHandle<MessageType>;\n hooks?: {\n onStart?: OnReactorStartHook<HandlerContext>;\n onClose?: OnReactorCloseHook;\n };\n};\n\nexport type HandlerOptions<\n MessageType extends AnyMessage = AnyMessage,\n MessageMetadataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n HandlerContext extends DefaultRecord = DefaultRecord,\n> =\n | {\n eachMessage: SingleRecordedMessageHandlerWithContext<\n MessageType,\n MessageMetadataType,\n HandlerContext\n >;\n eachBatch?: never;\n }\n | {\n eachMessage?: never;\n eachBatch: BatchRecordedMessageHandlerWithContext<\n MessageType,\n MessageMetadataType,\n HandlerContext\n >;\n };\n\nexport type OnReactorStartHook<\n HandlerContext extends DefaultRecord = DefaultRecord,\n> = (context: HandlerContext) => Promise<void>;\n\nexport type OnReactorCloseHook = () => Promise<void>;\n\nexport type ReactorOptions<\n MessageType extends AnyMessage = AnyMessage,\n MessageMetadataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n HandlerContext extends DefaultRecord = DefaultRecord,\n CheckpointType = GlobalPositionTypeOfRecordedMessageMetadata<MessageMetadataType>,\n> = BaseMessageProcessorOptions<\n MessageType,\n MessageMetadataType,\n HandlerContext,\n CheckpointType\n> &\n HandlerOptions<MessageType, MessageMetadataType, HandlerContext>;\n\nexport type ProjectorOptions<\n EventType extends AnyEvent = AnyEvent,\n MessageMetadataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n HandlerContext extends DefaultRecord = DefaultRecord,\n CheckpointType = GlobalPositionTypeOfRecordedMessageMetadata<MessageMetadataType>,\n> = Omit<\n BaseMessageProcessorOptions<\n EventType,\n MessageMetadataType,\n HandlerContext,\n CheckpointType\n >,\n 'type' | 'processorId'\n> & { processorId?: string } & {\n truncateOnStart?: boolean;\n projection: ProjectionDefinition<\n EventType,\n MessageMetadataType,\n HandlerContext\n >;\n};\n\nexport const defaultProcessingMessageProcessingScope = <\n HandlerContext = never,\n Result = MessageHandlerResult,\n>(\n handler: (context: HandlerContext) => Result | Promise<Result>,\n partialContext: Partial<HandlerContext>,\n) => handler(partialContext as HandlerContext);\n\nexport type ReadProcessorCheckpointResult<CheckpointType = unknown> = {\n lastCheckpoint: CheckpointType | null;\n};\n\nexport type ReadProcessorCheckpoint<\n CheckpointType = unknown,\n HandlerContext extends DefaultRecord = DefaultRecord,\n> = (\n options: { processorId: string; partition?: string },\n context: HandlerContext,\n) => Promise<ReadProcessorCheckpointResult<CheckpointType>>;\n\nexport type StoreProcessorCheckpointResult<CheckpointType = unknown> =\n | {\n success: true;\n newCheckpoint: CheckpointType;\n }\n | { success: false; reason: 'IGNORED' | 'MISMATCH' };\n\nexport type StoreProcessorCheckpoint<\n MessageType extends Message = AnyMessage,\n MessageMetadataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n CheckpointType = unknown,\n HandlerContext extends DefaultRecord | undefined = undefined,\n> =\n | ((\n options: {\n message: RecordedMessage<MessageType, MessageMetadataType>;\n processorId: string;\n version: number | undefined;\n lastCheckpoint: CheckpointType | null;\n partition?: string;\n },\n context: HandlerContext,\n ) => Promise<StoreProcessorCheckpointResult<CheckpointType | null>>)\n | ((\n options: {\n message: RecordedMessage<MessageType, MessageMetadataType>;\n processorId: string;\n version: number | undefined;\n lastCheckpoint: CheckpointType | null;\n partition?: string;\n },\n context: HandlerContext,\n ) => Promise<StoreProcessorCheckpointResult<CheckpointType>>);\n\nexport const reactor = <\n MessageType extends Message = AnyMessage,\n MessageMetadataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n HandlerContext extends DefaultRecord = DefaultRecord,\n CheckpointType = GlobalPositionTypeOfRecordedMessageMetadata<MessageMetadataType>,\n>(\n options: ReactorOptions<\n MessageType,\n MessageMetadataType,\n HandlerContext,\n CheckpointType\n >,\n): MessageProcessor<\n MessageType,\n MessageMetadataType,\n HandlerContext,\n CheckpointType\n> => {\n const eachMessage: SingleMessageHandlerWithContext<\n MessageType,\n MessageMetadataType,\n HandlerContext\n > =\n 'eachMessage' in options && options.eachMessage\n ? options.eachMessage\n : () => Promise.resolve();\n let isActive = true;\n\n const { checkpoints, processorId, partition } = options;\n\n const processingScope =\n options.processingScope ?? defaultProcessingMessageProcessingScope;\n\n let lastCheckpoint: CheckpointType | null = null;\n\n return {\n id: options.processorId,\n type: options.type ?? MessageProcessorType.REACTOR,\n close: () =>\n options.hooks?.onClose ? options.hooks?.onClose() : Promise.resolve(),\n start: async (\n startOptions: Partial<HandlerContext>,\n ): Promise<CurrentMessageProcessorPosition<CheckpointType> | undefined> => {\n isActive = true;\n\n if (lastCheckpoint !== null)\n return {\n lastCheckpoint,\n };\n\n return await processingScope(async (context) => {\n if (options.hooks?.onStart) {\n await options.hooks?.onStart(context);\n }\n\n if (options.startFrom !== 'CURRENT' && options.startFrom)\n return options.startFrom;\n\n if (checkpoints) {\n const readResult = await checkpoints?.read(\n {\n processorId: processorId,\n partition: partition,\n },\n { ...startOptions, ...context },\n );\n lastCheckpoint = readResult.lastCheckpoint;\n }\n\n if (lastCheckpoint === null) return 'BEGINNING';\n\n return {\n lastCheckpoint,\n };\n }, startOptions);\n },\n get isActive() {\n return isActive;\n },\n handle: async (\n messages: RecordedMessage<MessageType, MessageMetadataType>[],\n partialContext: Partial<HandlerContext>,\n ): Promise<MessageHandlerResult> => {\n if (!isActive) return Promise.resolve();\n\n return await processingScope(async (context) => {\n let result: MessageHandlerResult = undefined;\n\n for (const message of messages) {\n if (wasMessageHandled(message, lastCheckpoint)) continue;\n\n const messageProcessingResult = await eachMessage(message, context);\n\n if (checkpoints) {\n const storeCheckpointResult: StoreProcessorCheckpointResult<CheckpointType | null> =\n await checkpoints.store(\n {\n processorId: options.processorId,\n version: options.version,\n message,\n lastCheckpoint,\n partition: options.partition,\n },\n context,\n );\n\n if (storeCheckpointResult.success) {\n // TODO: Add correct handling of the storing checkpoint\n lastCheckpoint = storeCheckpointResult.newCheckpoint;\n }\n }\n\n if (\n messageProcessingResult &&\n messageProcessingResult.type === 'STOP'\n ) {\n isActive = false;\n result = messageProcessingResult;\n break;\n }\n\n if (options.stopAfter && options.stopAfter(message)) {\n isActive = false;\n result = { type: 'STOP', reason: 'Stop condition reached' };\n break;\n }\n\n if (\n messageProcessingResult &&\n messageProcessingResult.type === 'SKIP'\n )\n continue;\n }\n\n return result;\n }, partialContext);\n },\n };\n};\n\nexport const projector = <\n EventType extends Event = Event,\n EventMetaDataType extends\n AnyRecordedMessageMetadata = AnyRecordedMessageMetadata,\n HandlerContext extends DefaultRecord = DefaultRecord,\n CheckpointType = GlobalPositionTypeOfRecordedMessageMetadata<EventMetaDataType>,\n>(\n options: ProjectorOptions<\n EventType,\n EventMetaDataType,\n HandlerContext,\n CheckpointType\n >,\n): MessageProcessor<\n EventType,\n EventMetaDataType,\n HandlerContext,\n CheckpointType\n> => {\n const { projection, ...rest } = options;\n\n return reactor<EventType, EventMetaDataType, HandlerContext, CheckpointType>({\n ...rest,\n type: MessageProcessorType.PROJECTOR,\n processorId: options.processorId ?? `projection:${projection.name}`,\n hooks: {\n onStart:\n (options.truncateOnStart && options.projection.truncate) ||\n options.hooks?.onStart\n ? async (context: HandlerContext) => {\n if (options.truncateOnStart && options.projection.truncate)\n await options.projection.truncate(context);\n\n if (options.hooks?.onStart) await options.hooks?.onStart(context);\n }\n : undefined,\n onClose: options.hooks?.onClose,\n },\n eachMessage: async (\n event: RecordedMessage<EventType, EventMetaDataType>,\n context: HandlerContext,\n ) => {\n if (!projection.canHandle.includes(event.type)) return;\n\n await projection.handle([event], context);\n },\n });\n};\n","import { EmmettError } from '../errors';\nimport { JSONParser } from '../serialization';\nimport type {\n AnyEvent,\n AnyReadEventMetadata,\n BatchRecordedMessageHandlerWithContext,\n CanHandle,\n DefaultRecord,\n Event,\n} from '../typing';\nimport { arrayUtils } from '../utils';\n\nexport type ProjectionHandlingType = 'inline' | 'async';\n\nexport type ProjectionHandler<\n EventType extends Event = AnyEvent,\n EventMetaDataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n ProjectionHandlerContext extends DefaultRecord = DefaultRecord,\n> = BatchRecordedMessageHandlerWithContext<\n EventType,\n EventMetaDataType,\n ProjectionHandlerContext\n>;\n\nexport type TruncateProjection<\n ProjectionHandlerContext extends DefaultRecord = DefaultRecord,\n> = (context: ProjectionHandlerContext) => Promise<void>;\n\nexport interface ProjectionDefinition<\n EventType extends Event = AnyEvent,\n EventMetaDataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n ProjectionHandlerContext extends DefaultRecord = DefaultRecord,\n> {\n name?: string;\n canHandle: CanHandle<EventType>;\n handle: ProjectionHandler<\n EventType,\n EventMetaDataType,\n ProjectionHandlerContext\n >;\n truncate?: TruncateProjection<ProjectionHandlerContext>;\n}\n\nexport type ProjectionRegistration<\n HandlingType extends ProjectionHandlingType,\n ReadEventMetadataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n ProjectionHandlerContext extends DefaultRecord = DefaultRecord,\n> = {\n type: HandlingType;\n projection: ProjectionDefinition<\n AnyEvent,\n ReadEventMetadataType,\n ProjectionHandlerContext\n >;\n};\n\nexport const filterProjections = <\n ReadEventMetadataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n ProjectionHandlerContext extends DefaultRecord = DefaultRecord,\n>(\n type: ProjectionHandlingType,\n projections: ProjectionRegistration<\n ProjectionHandlingType,\n ReadEventMetadataType,\n ProjectionHandlerContext\n >[],\n) => {\n const inlineProjections = projections\n .filter((projection) => projection.type === type)\n .map(({ projection }) => projection);\n\n const duplicateRegistrations = arrayUtils.getDuplicates(\n inlineProjections,\n (proj) => proj.name,\n );\n\n if (duplicateRegistrations.length > 0) {\n throw new EmmettError(`You cannot register multiple projections with the same name (or without the name).\n Ensure that:\n ${JSONParser.stringify(duplicateRegistrations)}\n have different names`);\n }\n\n return inlineProjections;\n};\n\nexport const projection = <\n EventType extends Event = Event,\n EventMetaDataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n ProjectionHandlerContext extends DefaultRecord = DefaultRecord,\n>(\n definition: ProjectionDefinition<\n EventType,\n EventMetaDataType,\n ProjectionHandlerContext\n >,\n): ProjectionDefinition<\n EventType,\n EventMetaDataType,\n ProjectionHandlerContext\n> => definition;\n\nexport const inlineProjections = <\n ReadEventMetadataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n ProjectionHandlerContext extends DefaultRecord = DefaultRecord,\n>(\n definitions: ProjectionDefinition<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any,\n ReadEventMetadataType,\n ProjectionHandlerContext\n >[],\n): ProjectionRegistration<\n 'inline',\n ReadEventMetadataType,\n ProjectionHandlerContext\n>[] =>\n definitions.map((definition) => ({\n type: 'inline',\n projection: definition,\n }));\n\nexport const asyncProjections = <\n ReadEventMetadataType extends AnyReadEventMetadata = AnyReadEventMetadata,\n ProjectionHandlerContext extends DefaultRecord = DefaultRecord,\n>(\n definitions: ProjectionDefinition<\n AnyEvent,\n ReadEventMetadataType,\n ProjectionHandlerContext\n >[],\n): ProjectionRegistration<\n 'inline',\n ReadEventMetadataType,\n ProjectionHandlerContext\n>[] =>\n definitions.map((definition) => ({\n type: 'inline',\n projection: definition,\n }));\n\nexport const projections = {\n inline: inlineProjections,\n async: asyncProjections,\n};\n","import { filter } from './filter';\nimport { map } from './map';\nimport {\n notifyAboutNoActiveReadersStream,\n NotifyAboutNoActiveReadersStream,\n} from './notifyAboutNoActiveReaders';\nimport { reduce, ReduceTransformStream } from './reduce';\nimport { retryStream } from './retry';\nimport { skip, SkipTransformStream } from './skip';\nimport { stopAfter } from './stopAfter';\nimport { stopOn } from './stopOn';\nimport { take, TakeTransformStream } from './take';\nimport { waitAtMost } from './waitAtMost';\n\nexport const streamTransformations = {\n filter,\n take,\n TakeTransformStream,\n skip,\n SkipTransformStream,\n map,\n notifyAboutNoActiveReadersStream,\n NotifyAboutNoActiveReadersStream,\n reduce,\n ReduceTransformStream,\n retry: retryStream,\n stopAfter,\n stopOn,\n waitAtMost,\n};\n","import { type SQLExecutor } from '@event-driven-io/dumbo';\nimport type {\n BatchRecordedMessageHandlerWithoutContext,\n EmmettError,\n Message,\n ReadEventMetadataWithGlobalPosition,\n} from '@event-driven-io/emmett';\nimport { readLastMessageGlobalPosition } from '../../schema/readLastMessageGlobalPosition';\nimport {\n readMessagesBatch,\n type ReadMessagesBatchOptions,\n} from '../../schema/readMessagesBatch';\n\nexport const DefaultPostgreSQLEventStoreProcessorBatchSize = 100;\nexport const DefaultPostgreSQLEventStoreProcessorPullingFrequencyInMs = 50;\n\nexport type PostgreSQLEventStoreMessagesBatchHandlerResult = void | {\n type: 'STOP';\n reason?: string;\n error?: EmmettError;\n};\n\nexport type PostgreSQLEventStoreMessageBatchPullerOptions<\n MessageType extends Message = Message,\n> = {\n executor: SQLExecutor;\n pullingFrequencyInMs: number;\n batchSize: number;\n eachBatch: BatchRecordedMessageHandlerWithoutContext<\n MessageType,\n ReadEventMetadataWithGlobalPosition\n >;\n stopWhen?: {\n noMessagesLeft?: boolean;\n };\n};\n\nexport type PostgreSQLEventStoreMessageBatchPullerStartFrom =\n | { lastCheckpoint: bigint }\n | 'BEGINNING'\n | 'END';\n\nexport type PostgreSQLEventStoreMessageBatchPullerStartOptions = {\n startFrom: PostgreSQLEventStoreMessageBatchPullerStartFrom;\n};\n\nexport type PostgreSQLEventStoreMessageBatchPuller = {\n isRunning: boolean;\n start(\n options: PostgreSQLEventStoreMessageBatchPullerStartOptions,\n ): Promise<void>;\n stop(): Promise<void>;\n};\n\nexport const postgreSQLEventStoreMessageBatchPuller = <\n MessageType extends Message = Message,\n>({\n executor,\n batchSize,\n eachBatch,\n pullingFrequencyInMs,\n stopWhen,\n}: PostgreSQLEventStoreMessageBatchPullerOptions<MessageType>): PostgreSQLEventStoreMessageBatchPuller => {\n let isRunning = false;\n\n let start: Promise<void>;\n\n const pullMessages = async (\n options: PostgreSQLEventStoreMessageBatchPullerStartOptions,\n ) => {\n const after =\n options.startFrom === 'BEGINNING'\n ? 0n\n : options.startFrom === 'END'\n ? ((await readLastMessageGlobalPosition(executor))\n .currentGlobalPosition ?? 0n)\n : options.startFrom.lastCheckpoint;\n\n const readMessagesOptions: ReadMessagesBatchOptions = {\n after,\n batchSize,\n };\n\n let waitTime = 100;\n\n do {\n const { messages, currentGlobalPosition, areMessagesLeft } =\n await readMessagesBatch<MessageType>(executor, readMessagesOptions);\n\n if (messages.length > 0) {\n const result = await eachBatch(messages);\n\n if (result && result.type === 'STOP') {\n isRunning = false;\n break;\n }\n }\n\n readMessagesOptions.after = currentGlobalPosition;\n\n await new Promise((resolve) => setTimeout(resolve, waitTime));\n\n if (stopWhen?.noMessagesLeft === true && !areMessagesLeft) {\n isRunning = false;\n break;\n }\n\n if (!areMessagesLeft) {\n waitTime = Math.min(waitTime * 2, 1000);\n } else {\n waitTime = pullingFrequencyInMs;\n }\n } while (isRunning);\n };\n\n return {\n get isRunning() {\n return isRunning;\n },\n start: (options) => {\n if (isRunning) return start;\n\n start = (async () => {\n isRunning = true;\n\n return pullMessages(options);\n })();\n\n return start;\n },\n stop: async () => {\n if (!isRunning) return;\n isRunning = false;\n await start;\n },\n };\n};\n\nexport const zipPostgreSQLEventStoreMessageBatchPullerStartFrom = (\n options: (PostgreSQLEventStoreMessageBatchPullerStartFrom | undefined)[],\n): PostgreSQLEventStoreMessageBatchPullerStartFrom => {\n if (\n options.length === 0 ||\n options.some((o) => o === undefined || o === 'BEGINNING')\n )\n return 'BEGINNING';\n\n if (options.every((o) => o === 'END')) return 'END';\n\n return options\n .filter((o) => o !== undefined && o !== 'BEGINNING' && o !== 'END')\n .sort((a, b) => (a > b ? 1 : -1))[0]!;\n};\n","import { singleOrNull, sql, type SQLExecutor } from '@event-driven-io/dumbo';\nimport { defaultTag, messagesTable } from './typing';\n\ntype ReadLastMessageGlobalPositionSqlResult = {\n global_position: string;\n};\n\nexport type ReadLastMessageGlobalPositionResult = {\n currentGlobalPosition: bigint | null;\n};\n\nexport const readLastMessageGlobalPosition = async (\n execute: SQLExecutor,\n options?: { partition?: string },\n): Promise<ReadLastMessageGlobalPositionResult> => {\n const result = await singleOrNull(\n execute.query<ReadLastMessageGlobalPositionSqlResult>(\n sql(\n `SELECT global_position\n FROM ${messagesTable.name}\n WHERE partition = %L AND is_archived = FALSE AND transaction_id < pg_snapshot_xmin(pg_current_snapshot())\n ORDER BY transaction_id, global_position\n LIMIT 1`,\n options?.partition ?? defaultTag,\n ),\n ),\n );\n\n return {\n currentGlobalPosition:\n result !== null ? BigInt(result.global_position) : null,\n };\n};\n","export const emmettPrefix = 'emt';\n\nexport const globalTag = 'global';\nexport const defaultTag = 'emt:default';\n\nexport const globalNames = {\n module: `${emmettPrefix}:module:${globalTag}`,\n tenant: `${emmettPrefix}:tenant:${globalTag}`,\n};\n\nconst columns = {\n partition: {\n name: 'partition',\n },\n isArchived: { name: 'is_archived' },\n};\n\nexport const streamsTable = {\n name: `${emmettPrefix}_streams`,\n columns: {\n partition: columns.partition,\n isArchived: columns.isArchived,\n },\n};\n\nexport const messagesTable = {\n name: `${emmettPrefix}_messages`,\n columns: {\n partition: columns.partition,\n isArchived: columns.isArchived,\n },\n};\n\nexport const subscriptionsTable = {\n name: `${emmettPrefix}_subscriptions`,\n};\n","import { mapRows, sql, type SQLExecutor } from '@event-driven-io/dumbo';\nimport {\n type CombinedMessageMetadata,\n type Message,\n type MessageDataOf,\n type MessageMetaDataOf,\n type MessageTypeOf,\n type RecordedMessage,\n type RecordedMessageMetadata,\n type RecordedMessageMetadataWithGlobalPosition,\n} from '@event-driven-io/emmett';\nimport { defaultTag, messagesTable } from './typing';\n\ntype ReadMessagesBatchSqlResult<MessageType extends Message> = {\n stream_position: string;\n stream_id: string;\n message_data: MessageDataOf<MessageType>;\n message_metadata: MessageMetaDataOf<MessageType>;\n message_schema_version: string;\n message_type: MessageTypeOf<MessageType>;\n message_id: string;\n global_position: string;\n transaction_id: string;\n created: string;\n};\n\nexport type ReadMessagesBatchOptions =\n | {\n after: bigint;\n batchSize: number;\n }\n | {\n from: bigint;\n batchSize: number;\n }\n | { to: bigint; batchSize: number }\n | { from: bigint; to: bigint };\n\nexport type ReadMessagesBatchResult<\n MessageType extends Message,\n MessageMetadataType extends RecordedMessageMetadata = RecordedMessageMetadata,\n> = {\n currentGlobalPosition: bigint;\n messages: RecordedMessage<MessageType, MessageMetadataType>[];\n areMessagesLeft: boolean;\n};\n\nexport const readMessagesBatch = async <\n MessageType extends Message,\n RecordedMessageMetadataType extends\n RecordedMessageMetadataWithGlobalPosition = RecordedMessageMetadataWithGlobalPosition,\n>(\n execute: SQLExecutor,\n options: ReadMessagesBatchOptions & { partition?: string },\n): Promise<\n ReadMessagesBatchResult<MessageType, RecordedMessageMetadataType>\n> => {\n const from =\n 'from' in options\n ? options.from\n : 'after' in options\n ? options.after + 1n\n : 0n;\n const batchSize =\n options && 'batchSize' in options\n ? options.batchSize\n : options.to - options.from;\n\n const fromCondition: string =\n from !== -0n ? `AND global_position >= ${from}` : '';\n\n const toCondition =\n 'to' in options ? `AND global_position <= ${options.to}` : '';\n\n const limitCondition =\n 'batchSize' in options ? `LIMIT ${options.batchSize}` : '';\n\n const messages: RecordedMessage<MessageType, RecordedMessageMetadataType>[] =\n await mapRows(\n execute.query<ReadMessagesBatchSqlResult<MessageType>>(\n sql(\n `SELECT stream_id, stream_position, global_position, message_data, message_metadata, message_schema_version, message_type, message_id\n FROM ${messagesTable.name}\n WHERE partition = %L AND is_archived = FALSE AND transaction_id < pg_snapshot_xmin(pg_current_snapshot()) ${fromCondition} ${toCondition}\n ORDER BY transaction_id, global_position\n ${limitCondition}`,\n options?.partition ?? defaultTag,\n ),\n ),\n (row) => {\n const rawEvent = {\n type: row.message_type,\n data: row.message_data,\n metadata: row.message_metadata,\n } as unknown as MessageType;\n\n const metadata: RecordedMessageMetadataWithGlobalPosition = {\n ...('metadata' in rawEvent ? (rawEvent.metadata ?? {}) : {}),\n messageId: row.message_id,\n streamName: row.stream_id,\n streamPosition: BigInt(row.stream_position),\n globalPosition: BigInt(row.global_position),\n };\n\n return {\n ...rawEvent,\n kind: 'Event',\n metadata: metadata as CombinedMessageMetadata<\n MessageType,\n RecordedMessageMetadataType\n >,\n };\n },\n );\n\n return messages.length > 0\n ? {\n currentGlobalPosition:\n messages[messages.length - 1]!.metadata.globalPosition,\n messages: messages,\n areMessagesLeft: messages.length === batchSize,\n }\n : {\n currentGlobalPosition:\n 'from' in options\n ? options.from\n : 'after' in options\n ? options.after\n : 0n,\n messages: [],\n areMessagesLeft: false,\n };\n};\n","import { dumbo, type Dumbo, type SQLExecutor } from '@event-driven-io/dumbo';\nimport {\n EmmettError,\n MessageProcessor,\n type AnyEvent,\n type AnyMessage,\n type AnyRecordedMessageMetadata,\n type BatchRecordedMessageHandlerWithoutContext,\n type DefaultRecord,\n type Message,\n type MessageConsumer,\n type MessageConsumerOptions,\n type ReadEventMetadataWithGlobalPosition,\n} from '@event-driven-io/emmett';\nimport { v7 as uuid } from 'uuid';\nimport {\n DefaultPostgreSQLEventStoreProcessorBatchSize,\n DefaultPostgreSQLEventStoreProcessorPullingFrequencyInMs,\n postgreSQLEventStoreMessageBatchPuller,\n zipPostgreSQLEventStoreMessageBatchPullerStartFrom,\n type PostgreSQLEventStoreMessageBatchPuller,\n} from './messageBatchProcessing';\nimport {\n postgreSQLProjector,\n postgreSQLReactor,\n type PostgreSQLProcessor,\n type PostgreSQLProjectorOptions,\n type PostgreSQLReactorOptions,\n} from './postgreSQLProcessor';\n\nexport type PostgreSQLConsumerContext = {\n execute: SQLExecutor;\n connection: {\n connectionString: string;\n pool: Dumbo;\n };\n};\n\nexport type ExtendableContext = Partial<PostgreSQLConsumerContext> &\n DefaultRecord;\n\nexport type PostgreSQLEventStoreConsumerConfig<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ConsumerMessageType extends Message = any,\n> = MessageConsumerOptions<ConsumerMessageType> & {\n stopWhen?: {\n noMessagesLeft?: boolean;\n };\n pulling?: {\n batchSize?: number;\n pullingFrequencyInMs?: number;\n };\n};\n\nexport type PostgreSQLEventStoreConsumerOptions<\n ConsumerMessageType extends Message = Message,\n> = PostgreSQLEventStoreConsumerConfig<ConsumerMessageType> & {\n connectionString: string;\n pool?: Dumbo;\n};\n\nexport type PostgreSQLEventStoreConsumer<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ConsumerMessageType extends AnyMessage = any,\n> = MessageConsumer<ConsumerMessageType> &\n Readonly<{\n reactor: <MessageType extends AnyMessage = ConsumerMessageType>(\n options: PostgreSQLReactorOptions<MessageType>,\n ) => PostgreSQLProcessor<MessageType>;\n }> &\n (AnyEvent extends ConsumerMessageType\n ? Readonly<{\n projector: <\n EventType extends AnyEvent = ConsumerMessageType & AnyEvent,\n >(\n options: PostgreSQLProjectorOptions<EventType>,\n ) => PostgreSQLProcessor<EventType>;\n }>\n : object);\n\nexport const postgreSQLEventStoreConsumer = <\n ConsumerMessageType extends Message = AnyMessage,\n>(\n options: PostgreSQLEventStoreConsumerOptions<ConsumerMessageType>,\n): PostgreSQLEventStoreConsumer<ConsumerMessageType> => {\n let isRunning = false;\n const { pulling } = options;\n const processors = options.processors ?? [];\n\n let start: Promise<void>;\n\n let currentMessagePuller: PostgreSQLEventStoreMessageBatchPuller | undefined;\n\n const pool = options.pool\n ? options.pool\n : dumbo({ connectionString: options.connectionString });\n\n const eachBatch: BatchRecordedMessageHandlerWithoutContext<\n ConsumerMessageType,\n ReadEventMetadataWithGlobalPosition\n > = async (messagesBatch) => {\n const activeProcessors = processors.filter((s) => s.isActive);\n\n if (activeProcessors.length === 0)\n return {\n type: 'STOP',\n reason: 'No active processors',\n };\n\n const result = await Promise.allSettled(\n activeProcessors.map((s) => {\n // TODO: Add here filtering to only pass messages that can be handled by processor\n return s.handle(messagesBatch, {\n connection: {\n connectionString: options.connectionString,\n pool,\n },\n });\n }),\n );\n\n return result.some(\n (r) => r.status === 'fulfilled' && r.value?.type !== 'STOP',\n )\n ? undefined\n : {\n type: 'STOP',\n };\n };\n\n const messagePooler = (currentMessagePuller =\n postgreSQLEventStoreMessageBatchPuller({\n stopWhen: options.stopWhen,\n executor: pool.execute,\n eachBatch,\n batchSize:\n pulling?.batchSize ?? DefaultPostgreSQLEventStoreProcessorBatchSize,\n pullingFrequencyInMs:\n pulling?.pullingFrequencyInMs ??\n DefaultPostgreSQLEventStoreProcessorPullingFrequencyInMs,\n }));\n\n const stop = async () => {\n if (!isRunning) return;\n isRunning = false;\n if (currentMessagePuller) {\n await currentMessagePuller.stop();\n currentMessagePuller = undefined;\n }\n await start;\n\n await Promise.all(processors.map((p) => p.close()));\n };\n\n return {\n consumerId: options.consumerId ?? uuid(),\n get isRunning() {\n return isRunning;\n },\n processors,\n reactor: <MessageType extends AnyMessage = ConsumerMessageType>(\n options: PostgreSQLReactorOptions<MessageType>,\n ): PostgreSQLProcessor<MessageType> => {\n const processor = postgreSQLReactor(options);\n\n processors.push(\n // TODO: change that\n processor as unknown as MessageProcessor<\n ConsumerMessageType,\n AnyRecordedMessageMetadata,\n DefaultRecord\n >,\n );\n\n return processor;\n },\n projector: <EventType extends AnyEvent = ConsumerMessageType & AnyEvent>(\n options: PostgreSQLProjectorOptions<EventType>,\n ): PostgreSQLProcessor<EventType> => {\n const processor = postgreSQLProjector(options);\n\n processors.push(\n // TODO: change that\n processor as unknown as MessageProcessor<\n ConsumerMessageType,\n AnyRecordedMessageMetadata,\n DefaultRecord\n >,\n );\n\n return processor;\n },\n start: () => {\n if (isRunning) return start;\n\n start = (async () => {\n if (processors.length === 0)\n return Promise.reject(\n new EmmettError(\n 'Cannot start consumer without at least a single processor',\n ),\n );\n\n isRunning = true;\n\n const startFrom = zipPostgreSQLEventStoreMessageBatchPullerStartFrom(\n await Promise.all(\n processors.map(async (o) => {\n const result = await o.start({\n execute: pool.execute,\n connection: {\n connectionString: options.connectionString,\n pool,\n },\n });\n\n return result;\n }),\n ),\n );\n\n return messagePooler.start({ startFrom });\n })();\n\n return start;\n },\n stop,\n close: async () => {\n await stop();\n await pool.close();\n },\n };\n};\n","import {\n dumbo,\n type Dumbo,\n type NodePostgresClient,\n type NodePostgresClientConnection,\n type NodePostgresConnector,\n type NodePostgresPool,\n type NodePostgresPoolClientConnection,\n type NodePostgresTransaction,\n type SQLExecutor,\n} from '@event-driven-io/dumbo';\nimport {\n EmmettError,\n getCheckpoint,\n MessageProcessor,\n projector,\n reactor,\n type AnyEvent,\n type AnyMessage,\n type BatchRecordedMessageHandlerWithContext,\n type Checkpointer,\n type Event,\n type Message,\n type MessageHandlerResult,\n type MessageProcessingScope,\n type ProjectorOptions,\n type ReactorOptions,\n type ReadEventMetadataWithGlobalPosition,\n type SingleRecordedMessageHandlerWithContext,\n} from '@event-driven-io/emmett';\nimport pg from 'pg';\nimport { readProcessorCheckpoint, storeProcessorCheckpoint } from '../schema';\nimport type { PostgreSQLEventStoreMessageBatchPullerStartFrom } from './messageBatchProcessing';\n\nexport type PostgreSQLProcessorHandlerContext = {\n execute: SQLExecutor;\n connection: {\n connectionString: string;\n client: NodePostgresClient;\n transaction: NodePostgresTransaction;\n pool: Dumbo;\n };\n};\n\nexport type PostgreSQLProcessor<MessageType extends Message = AnyMessage> =\n MessageProcessor<\n MessageType,\n ReadEventMetadataWithGlobalPosition,\n PostgreSQLProcessorHandlerContext\n >;\n\nexport type PostgreSQLProcessorEachMessageHandler<\n MessageType extends Message = Message,\n> = SingleRecordedMessageHandlerWithContext<\n MessageType,\n ReadEventMetadataWithGlobalPosition,\n PostgreSQLProcessorHandlerContext\n>;\n\nexport type PostgreSQLProcessorEachBatchHandler<\n MessageType extends Message = Message,\n> = BatchRecordedMessageHandlerWithContext<\n MessageType,\n ReadEventMetadataWithGlobalPosition,\n PostgreSQLProcessorHandlerContext\n>;\n\nexport type PostgreSQLProcessorStartFrom =\n | PostgreSQLEventStoreMessageBatchPullerStartFrom\n | 'CURRENT';\n\ntype PostgreSQLProcessorPooledOptions =\n | {\n connector?: NodePostgresConnector;\n database?: string;\n pooled: true;\n pool: pg.Pool;\n }\n | {\n connector?: NodePostgresConnector;\n database?: string;\n pool: pg.Pool;\n }\n | {\n connector?: NodePostgresConnector;\n database?: string;\n pooled: true;\n }\n | {\n connector?: NodePostgresConnector;\n database?: string;\n };\n\ntype PostgreSQLProcessorNotPooledOptions =\n | {\n connector?: NodePostgresConnector;\n database?: string;\n pooled: false;\n client: pg.Client;\n }\n | {\n connector?: NodePostgresConnector;\n database?: string;\n client: pg.Client;\n }\n | {\n connector?: NodePostgresConnector;\n database?: string;\n pooled: false;\n }\n | {\n connector?: NodePostgresConnector;\n database?: string;\n connection:\n | NodePostgresPoolClientConnection\n | NodePostgresClientConnection;\n pooled?: false;\n }\n | {\n connector?: NodePostgresConnector;\n database?: string;\n dumbo: NodePostgresPool;\n pooled?: false;\n };\n\nexport type PostgreSQLProcessorConnectionOptions = {\n connectionString: string;\n} & (PostgreSQLProcessorPooledOptions | PostgreSQLProcessorNotPooledOptions);\n\nexport type PostgreSQLCheckpointer<\n MessageType extends AnyMessage = AnyMessage,\n> = Checkpointer<\n MessageType,\n ReadEventMetadataWithGlobalPosition,\n PostgreSQLProcessorHandlerContext\n>;\n\nexport const postgreSQLCheckpointer = <\n MessageType extends Message = Message,\n>(): PostgreSQLCheckpointer<MessageType> => ({\n read: async (options, context) => {\n const result = await readProcessorCheckpoint(context.execute, options);\n\n return { lastCheckpoint: result?.lastProcessedPosition };\n },\n store: async (options, context) => {\n const newPosition: bigint | null = getCheckpoint(options.message);\n\n const result = await storeProcessorCheckpoint(context.execute, {\n lastProcessedPosition: options.lastCheckpoint,\n newPosition,\n processorId: options.processorId,\n partition: options.partition,\n version: options.version,\n });\n\n return result.success\n ? { success: true, newCheckpoint: result.newPosition }\n : result;\n },\n});\n\ntype PostgreSQLConnectionOptions = {\n connectionOptions?: PostgreSQLProcessorConnectionOptions;\n};\n\nexport type PostgreSQLReactorOptions<MessageType extends Message = Message> =\n ReactorOptions<\n MessageType,\n ReadEventMetadataWithGlobalPosition,\n PostgreSQLProcessorHandlerContext\n > &\n PostgreSQLConnectionOptions;\n\nexport type PostgreSQLProjectorOptions<EventType extends AnyEvent = AnyEvent> =\n ProjectorOptions<\n EventType,\n ReadEventMetadataWithGlobalPosition,\n PostgreSQLProcessorHandlerContext\n > &\n PostgreSQLConnectionOptions;\n\nexport type PostgreSQLProcessorOptions<\n MessageType extends AnyMessage = AnyMessage,\n> =\n | PostgreSQLReactorOptions<MessageType>\n | PostgreSQLProjectorOptions<MessageType & AnyEvent>;\n\nconst postgreSQLProcessingScope = (options: {\n pool: Dumbo | null;\n connectionString: string | null;\n processorId: string;\n}): MessageProcessingScope<PostgreSQLProcessorHandlerContext> => {\n const processorConnectionString = options.connectionString;\n\n const processorPool = options.pool;\n\n const processingScope: MessageProcessingScope<\n PostgreSQLProcessorHandlerContext\n > = async <Result = MessageHandlerResult>(\n handler: (\n context: PostgreSQLProcessorHandlerContext,\n ) => Result | Promise<Result>,\n partialContext: Partial<PostgreSQLProcessorHandlerContext>,\n ) => {\n const connection = partialContext?.connection;\n const connectionString =\n processorConnectionString ?? connection?.connectionString;\n\n if (!connectionString)\n throw new EmmettError(\n `PostgreSQL processor '${options.processorId}' is missing connection string. Ensure that you passed it through options`,\n );\n\n const pool =\n (!processorConnectionString ||\n connectionString == processorConnectionString\n ? connection?.pool\n : processorPool) ?? processorPool;\n\n if (!pool)\n throw new EmmettError(\n `PostgreSQL processor '${options.processorId}' is missing connection string. Ensure that you passed it through options`,\n );\n\n return pool.withTransaction(async (transaction) => {\n const client =\n (await transaction.connection.open()) as NodePostgresClient;\n return handler({\n ...partialContext,\n execute: transaction.execute,\n connection: {\n connectionString,\n pool,\n client,\n transaction,\n },\n });\n });\n };\n\n return processingScope;\n};\n\nconst getProcessorPool = (options: PostgreSQLConnectionOptions) => {\n const poolOptions = {\n ...(options.connectionOptions ? options.connectionOptions : {}),\n };\n const processorConnectionString =\n 'connectionString' in poolOptions\n ? (poolOptions.connectionString ?? null)\n : null;\n\n const processorPool =\n 'dumbo' in poolOptions\n ? (poolOptions.dumbo as NodePostgresPool)\n : processorConnectionString\n ? dumbo({\n connectionString: processorConnectionString,\n ...poolOptions,\n })\n : null;\n\n return {\n pool: processorPool,\n connectionString: processorConnectionString,\n close:\n processorPool != null && !('dumbo' in poolOptions)\n ? processorPool.close\n : undefined,\n };\n};\n\nexport const postgreSQLProjector = <EventType extends Event = Event>(\n options: PostgreSQLProjectorOptions<EventType>,\n): PostgreSQLProcessor<EventType> => {\n const { pool, connectionString, close } = getProcessorPool(options);\n\n const hooks = {\n onStart: options.hooks?.onStart,\n onClose:\n options.hooks?.onClose || close\n ? async () => {\n if (options.hooks?.onClose) await options.hooks?.onClose();\n if (close) await close();\n }\n : undefined,\n };\n\n return projector<\n EventType,\n ReadEventMetadataWithGlobalPosition,\n PostgreSQLProcessorHandlerContext\n >({\n ...options,\n hooks,\n processingScope: postgreSQLProcessingScope({\n pool,\n connectionString,\n processorId:\n options.processorId ?? `projection:${options.projection.name}`,\n }),\n checkpoints: postgreSQLCheckpointer<EventType>(),\n });\n};\n\nexport const postgreSQLReactor = <MessageType extends Message = Message>(\n options: PostgreSQLReactorOptions<MessageType>,\n): PostgreSQLProcessor<MessageType> => {\n const { pool, connectionString, close } = getProcessorPool(options);\n\n const hooks = {\n onStart: options.hooks?.onStart,\n onClose:\n options.hooks?.onClose || close\n ? async () => {\n if (options.hooks?.onClose) await options.hooks?.onClose();\n if (close) await close();\n }\n : undefined,\n };\n\n return reactor({\n ...options,\n hooks,\n processingScope: postgreSQLProcessingScope({\n pool,\n connectionString,\n processorId: options.processorId,\n }),\n checkpoints: postgreSQLCheckpointer<MessageType>(),\n });\n};\n\nexport const postgreSQLMessageProcessor = <\n MessageType extends AnyMessage = AnyMessage,\n>(\n options: PostgreSQLProcessorOptions<MessageType>,\n): PostgreSQLProcessor<MessageType> => {\n if ('projection' in options) {\n return postgreSQLProjector(\n options as unknown as PostgreSQLProjectorOptions<Event>,\n ) as PostgreSQLProcessor<MessageType>;\n }\n\n return postgreSQLReactor(options);\n};\n","import { type NodePostgresPool, type SQL } from '@event-driven-io/dumbo';\nimport {\n appendToStreamSQL,\n dropOldAppendToSQLWithoutGlobalPositions,\n} from './appendToStream';\nimport { storeSubscriptionCheckpointSQL } from './storeProcessorCheckpoint';\nimport {\n addDefaultPartitionSQL,\n addPartitionSQL,\n addTablePartitions,\n dropFutureConceptModuleAndTenantFunctions,\n messagesTableSQL,\n migrationFromEventsToMessagesSQL,\n sanitizeNameSQL,\n streamsTableSQL,\n subscriptionsTableSQL,\n} from './tables';\n\nexport * from './appendToStream';\nexport * from './readLastMessageGlobalPosition';\nexport * from './readMessagesBatch';\nexport * from './readProcessorCheckpoint';\nexport * from './readStream';\nexport * from './storeProcessorCheckpoint';\nexport * from './tables';\nexport * from './typing';\n\nexport const schemaSQL: SQL[] = [\n migrationFromEventsToMessagesSQL,\n streamsTableSQL,\n messagesTableSQL,\n subscriptionsTableSQL,\n sanitizeNameSQL,\n addTablePartitions,\n addPartitionSQL,\n dropFutureConceptModuleAndTenantFunctions,\n //addModuleSQL,\n //addTenantSQL,\n //addModuleForAllTenantsSQL,\n //addTenantForAllModulesSQL,\n dropOldAppendToSQLWithoutGlobalPositions,\n appendToStreamSQL,\n addDefaultPartitionSQL,\n storeSubscriptionCheckpointSQL,\n];\n\nexport const createEventStoreSchema = async (\n pool: NodePostgresPool,\n): Promise<void> => {\n await pool.withTransaction(({ execute }) => execute.batchCommand(schemaSQL));\n};\n","import {\n rawSql,\n single,\n SQL,\n sql,\n type NodePostgresPool,\n type NodePostgresTransaction,\n type SQLExecutor,\n} from '@event-driven-io/dumbo';\nimport {\n JSONParser,\n NO_CONCURRENCY_CHECK,\n STREAM_DOES_NOT_EXIST,\n STREAM_EXISTS,\n type AppendToStreamOptions,\n type ExpectedStreamVersion,\n type Message,\n type RecordedMessage,\n} from '@event-driven-io/emmett';\nimport { v4 as uuid } from 'uuid';\nimport { defaultTag, messagesTable, streamsTable } from './typing';\n\nexport const appendToStreamSQL = rawSql(\n `CREATE OR REPLACE FUNCTION emt_append_to_stream(\n v_message_ids text[],\n v_messages_data jsonb[],\n v_messages_metadata jsonb[],\n v_message_schema_versions text[],\n v_message_types text[],\n v_message_kinds text[],\n v_stream_id text,\n v_stream_type text,\n v_expected_stream_position bigint DEFAULT NULL,\n v_partition text DEFAULT emt_sanitize_name('default_partition')\n ) RETURNS TABLE (\n success boolean,\n next_stream_position bigint,\n global_positions bigint[],\n transaction_id xid8\n ) LANGUAGE plpgsql\n AS $$\n DECLARE\n v_next_stream_position bigint;\n v_position bigint;\n v_updated_rows int;\n v_transaction_id xid8;\n v_global_positions bigint[];\n BEGIN\n v_transaction_id := pg_current_xact_id();\n\n IF v_expected_stream_position IS NULL THEN\n SELECT COALESCE(\n (SELECT stream_position \n FROM ${streamsTable.name}\n WHERE stream_id = v_stream_id \n AND partition = v_partition \n AND is_archived = FALSE\n LIMIT 1), \n 0\n ) INTO v_expected_stream_position;\n END IF;\n\n v_next_stream_position := v_expected_stream_position + array_upper(v_messages_data, 1);\n\n IF v_expected_stream_position = 0 THEN\n INSERT INTO ${streamsTable.name}\n (stream_id, stream_position, partition, stream_type, stream_metadata, is_archived)\n VALUES\n (v_stream_id, v_next_stream_position, v_partition, v_stream_type, '{}', FALSE);\n ELSE\n UPDATE ${streamsTable.name} as s \n SET stream_position = v_next_stream_position\n WHERE stream_id = v_stream_id AND stream_position = v_expected_stream_position AND partition = v_partition AND is_archived = FALSE;\n\n get diagnostics v_updated_rows = row_count;\n\n IF v_updated_rows = 0 THEN\n RETURN QUERY SELECT FALSE, NULL::bigint, NULL::bigint[], NULL::xid8;\n RETURN;\n END IF;\n END IF;\n\n WITH ev AS (\n SELECT row_number() OVER () + v_expected_stream_position AS stream_position, \n message_data, \n message_metadata, \n schema_version, \n message_id, \n message_type,\n message_kind\n FROM (\n SELECT *\n FROM \n unnest(v_message_ids, v_messages_data, v_messages_metadata, v_message_schema_versions, v_message_types, v_message_kinds) \n AS message(message_id, message_data, message_metadata, schema_version, message_type, message_kind)\n ) AS message\n ),\n all_messages_insert AS (\n INSERT INTO ${messagesTable.name}\n (stream_id, stream_position, partition, message_data, message_metadata, message_schema_version, message_type, message_kind, message_id, transaction_id)\n SELECT \n v_stream_id, ev.stream_position, v_partition, ev.message_data, ev.message_metadata, ev.schema_version, ev.message_type, ev.message_kind, ev.message_id, v_transaction_id\n FROM ev\n RETURNING global_position\n )\n SELECT \n array_agg(global_position ORDER BY global_position) INTO v_global_positions\n FROM \n all_messages_insert;\n\n RETURN QUERY SELECT TRUE, v_next_stream_position, v_global_positions, v_transaction_id;\n END;\n $$;\n `,\n);\n\nexport const dropOldAppendToSQLWithoutGlobalPositions = SQL`\n DO $$\n DECLARE\n v_current_return_type text;\n BEGIN\n -- Get the current return type definition as text\n SELECT pg_get_function_result(p.oid)\n INTO v_current_return_type\n FROM pg_proc p\n JOIN pg_namespace n ON p.pronamespace = n.oid\n WHERE n.nspname = current_schema() -- or specify your schema\n AND p.proname = 'emt_append_to_stream'\n AND p.pronargs = 10; -- number of arguments\n \n -- Check if it contains the old column name\n IF v_current_return_type IS NOT NULL AND \n v_current_return_type LIKE '%last_global_position%' AND \n v_current_return_type NOT LIKE '%global_positions%' THEN\n DROP FUNCTION emt_append_to_stream(text[], jsonb[], jsonb[], text[], text[], text[], text, text, bigint, text);\n RAISE NOTICE 'Old version of function dropped. Return type was: %', v_current_return_type;\n END IF;\n END $$;\n`;\n\ntype AppendToStreamResult =\n | {\n success: true;\n nextStreamPosition: bigint;\n globalPositions: bigint[];\n transactionId: string;\n }\n | { success: false };\n\nexport type AppendToStreamBeforeCommitHook = (\n messages: RecordedMessage[],\n context: {\n transaction: NodePostgresTransaction;\n },\n) => Promise<void>;\n\nexport const appendToStream = (\n pool: NodePostgresPool,\n streamName: string,\n streamType: string,\n messages: Message[],\n options?: AppendToStreamOptions & {\n partition?: string;\n beforeCommitHook?: AppendToStreamBeforeCommitHook;\n },\n): Promise<AppendToStreamResult> =>\n pool.withTransaction<AppendToStreamResult>(async (transaction) => {\n const { execute } = transaction;\n\n if (messages.length === 0)\n return { success: false, result: { success: false } };\n\n try {\n const expectedStreamVersion = toExpectedVersion(\n options?.expectedStreamVersion,\n );\n\n const messagesToAppend: RecordedMessage[] = messages.map((e) => ({\n ...e,\n kind: e.kind ?? 'Event',\n metadata: {\n messageId: uuid(),\n ...('metadata' in e ? (e.metadata ?? {}) : {}),\n },\n })) as RecordedMessage[];\n\n const {\n success,\n next_stream_position,\n global_positions,\n transaction_id,\n } = await appendEventsRaw(\n execute,\n streamName,\n streamType,\n messagesToAppend,\n {\n expectedStreamVersion,\n },\n );\n\n if (\n !success ||\n next_stream_position === null ||\n global_positions === null ||\n global_positions.length === 0 ||\n transaction_id == null\n ) {\n return {\n success: false,\n\n result: { success: false },\n };\n }\n\n const nextStreamPosition = BigInt(next_stream_position);\n\n const globalPositions = global_positions.map(BigInt);\n\n globalPositions.forEach((globalPosition, index) => {\n messagesToAppend[index]!.metadata = {\n ...messagesToAppend[index]!.metadata,\n streamName,\n streamPosition:\n nextStreamPosition -\n BigInt(messagesToAppend.length) +\n BigInt(index + 1),\n globalPosition,\n };\n });\n\n if (options?.beforeCommitHook)\n await options.beforeCommitHook(messagesToAppend, { transaction });\n\n return {\n success: true,\n\n result: {\n success: true,\n nextStreamPosition,\n globalPositions,\n transactionId: transaction_id,\n },\n };\n } catch (error) {\n if (!isOptimisticConcurrencyError(error)) throw error;\n\n return {\n success: false,\n result: { success: false },\n };\n }\n });\n\nconst toExpectedVersion = (\n expected: ExpectedStreamVersion | undefined,\n): bigint | null => {\n if (expected === undefined) return null;\n\n if (expected === NO_CONCURRENCY_CHECK) return null;\n\n // TODO: this needs to be fixed\n if (expected == STREAM_DOES_NOT_EXIST) return null;\n\n // TODO: this needs to be fixed\n if (expected == STREAM_EXISTS) return null;\n\n return expected as bigint;\n};\n\nconst isOptimisticConcurrencyError = (error: unknown): boolean =>\n error instanceof Error && 'code' in error && error.code === '23505';\n\ntype AppendToStreamSqlResult = {\n success: boolean;\n next_stream_position: string | null;\n global_positions: string[] | null;\n transaction_id: string | null | undefined;\n};\n\nconst appendEventsRaw = (\n execute: SQLExecutor,\n streamId: string,\n streamType: string,\n messages: RecordedMessage[],\n options?: {\n expectedStreamVersion: bigint | null;\n partition?: string;\n },\n): Promise<AppendToStreamSqlResult> =>\n single(\n execute.command<AppendToStreamSqlResult>(\n sql(\n `SELECT * FROM emt_append_to_stream(\n ARRAY[%s]::text[],\n ARRAY[%s]::jsonb[],\n ARRAY[%s]::jsonb[],\n ARRAY[%s]::text[],\n ARRAY[%s]::text[],\n ARRAY[%s]::text[],\n %L::text,\n %L::text,\n %s::bigint,\n %L::text\n )`,\n messages.map((e) => sql('%L', e.metadata.messageId)).join(','),\n messages.map((e) => sql('%L', JSONParser.stringify(e.data))).join(','),\n messages\n .map((e) => {\n const { messageId: _messageId, ...rawMetadata } = e.metadata;\n return sql('%L', JSONParser.stringify(rawMetadata));\n })\n .join(','),\n messages.map(() => `'1'`).join(','),\n messages.map((e) => sql('%L', e.type)).join(','),\n messages\n .map((e) => sql('%L', e.kind === 'Event' ? 'E' : 'C'))\n .join(','),\n streamId,\n streamType,\n options?.expectedStreamVersion ?? 'NULL',\n options?.partition ?? defaultTag,\n ),\n ),\n );\n","import { single, sql, type SQLExecutor } from '@event-driven-io/dumbo';\nimport { defaultTag, subscriptionsTable } from './typing';\n\nexport const storeSubscriptionCheckpointSQL = sql(`\nCREATE OR REPLACE FUNCTION store_subscription_checkpoint(\n p_subscription_id VARCHAR(100),\n p_version BIGINT,\n p_position BIGINT,\n p_check_position BIGINT,\n p_transaction_id xid8,\n p_partition TEXT DEFAULT '${defaultTag}'\n) RETURNS INT AS $$\nDECLARE\n current_position BIGINT;\nBEGIN\n -- Handle the case when p_check_position is provided\n IF p_check_position IS NOT NULL THEN\n -- Try to update if the position matches p_check_position\n UPDATE \"${subscriptionsTable.name}\"\n SET \n \"last_processed_position\" = p_position, \n \"last_processed_transaction_id\" = p_transaction_id\n WHERE \"subscription_id\" = p_subscription_id AND \"last_processed_position\" = p_check_position AND \"partition\" = p_partition;\n\n IF FOUND THEN\n RETURN 1; -- Successfully updated\n END IF;\n\n -- Retrieve the current position\n SELECT \"last_processed_position\" INTO current_position\n FROM \"${subscriptionsTable.name}\"\n WHERE \"subscription_id\" = p_subscription_id AND \"partition\" = p_partition;\n\n -- Return appropriate codes based on current position\n IF current_position = p_position THEN\n RETURN 0; -- Idempotent check: position already set\n ELSIF current_position > p_check_position THEN\n RETURN 2; -- Failure: current position is greater\n ELSE\n RETURN 2; -- Default failure case for mismatched positions\n END IF;\n END IF;\n\n -- Handle the case when p_check_position is NULL: Insert if not exists\n BEGIN\n INSERT INTO \"${subscriptionsTable.name}\"(\"subscription_id\", \"version\", \"last_processed_position\", \"partition\", \"last_processed_transaction_id\")\n VALUES (p_subscription_id, p_version, p_position, p_partition, p_transaction_id);\n RETURN 1; -- Successfully inserted\n EXCEPTION WHEN unique_violation THEN\n -- If insertion failed, it means the row already exists\n SELECT \"last_processed_position\" INTO current_position\n FROM \"${subscriptionsTable.name}\"\n WHERE \"subscription_id\" = p_subscription_id AND \"partition\" = p_partition;\n\n IF current_position = p_position THEN\n RETURN 0; -- Idempotent check: position already set\n ELSE\n RETURN 2; -- Insertion failed, row already exists with different position\n END IF;\n END;\nEND;\n$$ LANGUAGE plpgsql;\n`);\n\nexport type StoreLastProcessedProcessorPositionResult<\n Position extends bigint | null = bigint,\n> =\n | {\n success: true;\n newPosition: Position;\n }\n | { success: false; reason: 'IGNORED' | 'MISMATCH' };\n\nexport const storeProcessorCheckpoint = async <Position extends bigint | null>(\n execute: SQLExecutor,\n options: {\n processorId: string;\n version: number | undefined;\n newPosition: null extends Position ? bigint | null : bigint;\n lastProcessedPosition: bigint | null;\n partition?: string;\n },\n): Promise<\n StoreLastProcessedProcessorPositionResult<\n null extends Position ? bigint | null : bigint\n >\n> => {\n try {\n const { result } = await single(\n execute.command<{ result: 0 | 1 | 2 }>(\n sql(\n `SELECT store_subscription_checkpoint(%L, %s, %L, %L, pg_current_xact_id(), %L) as result;`,\n options.processorId,\n options.version ?? 1,\n options.newPosition,\n options.lastProcessedPosition,\n options.partition ?? defaultTag,\n ),\n ),\n );\n\n return result === 1\n ? { success: true, newPosition: options.newPosition }\n : { success: false, reason: result === 0 ? 'IGNORED' : 'MISMATCH' };\n } catch (error) {\n console.log(error);\n throw error;\n }\n};\n","import { rawSql, SQL } from '@event-driven-io/dumbo';\nimport {\n defaultTag,\n globalTag,\n messagesTable,\n streamsTable,\n subscriptionsTable,\n} from './typing';\n\nexport const streamsTableSQL = rawSql(\n `CREATE TABLE IF NOT EXISTS ${streamsTable.name}(\n stream_id TEXT NOT NULL,\n stream_position BIGINT NOT NULL,\n partition TEXT NOT NULL DEFAULT '${globalTag}',\n stream_type TEXT NOT NULL,\n stream_metadata JSONB NOT NULL,\n is_archived BOOLEAN NOT NULL DEFAULT FALSE,\n PRIMARY KEY (stream_id, partition, is_archived)\n ) PARTITION BY LIST (partition);\n \n CREATE UNIQUE INDEX IF NOT EXISTS idx_streams_unique \n ON ${streamsTable.name}(stream_id, partition, is_archived) \n INCLUDE (stream_position);`,\n);\n\nexport const messagesTableSQL = rawSql(\n `\n CREATE SEQUENCE IF NOT EXISTS emt_global_message_position;\n\n CREATE TABLE IF NOT EXISTS ${messagesTable.name}(\n stream_id TEXT NOT NULL,\n stream_position BIGINT NOT NULL,\n partition TEXT NOT NULL DEFAULT '${globalTag}',\n message_kind CHAR(1) NOT NULL DEFAULT 'E',\n message_data JSONB NOT NULL,\n message_metadata JSONB NOT NULL,\n message_schema_version TEXT NOT NULL,\n message_type TEXT NOT NULL,\n message_id TEXT NOT NULL,\n is_archived BOOLEAN NOT NULL DEFAULT FALSE,\n global_position BIGINT DEFAULT nextval('emt_global_message_position'),\n transaction_id XID8 NOT NULL,\n created TIMESTAMPTZ NOT NULL DEFAULT now(),\n PRIMARY KEY (stream_id, stream_position, partition, is_archived)\n ) PARTITION BY LIST (partition);`,\n);\n\nexport const subscriptionsTableSQL = rawSql(\n `\n CREATE TABLE IF NOT EXISTS ${subscriptionsTable.name}(\n subscription_id TEXT NOT NULL,\n version INT NOT NULL DEFAULT 1,\n partition TEXT NOT NULL DEFAULT '${globalTag}',\n last_processed_position BIGINT NOT NULL,\n last_processed_transaction_id XID8 NOT NULL,\n PRIMARY KEY (subscription_id, partition, version)\n ) PARTITION BY LIST (partition);\n`,\n);\n\nexport const sanitizeNameSQL = rawSql(\n `CREATE OR REPLACE FUNCTION emt_sanitize_name(input_name TEXT) RETURNS TEXT AS $$\n BEGIN\n RETURN REGEXP_REPLACE(input_name, '[^a-zA-Z0-9_]', '_', 'g');\n END;\n $$ LANGUAGE plpgsql;`,\n);\n\nexport const addTablePartitions = rawSql(\n `\n CREATE OR REPLACE FUNCTION emt_add_table_partition(tableName TEXT, partition_name TEXT) RETURNS void AS $$\n DECLARE\n v_main_partiton_name TEXT;\n v_active_partiton_name TEXT;\n v_archived_partiton_name TEXT;\n BEGIN \n v_main_partiton_name := emt_sanitize_name(tableName || '_' || partition_name);\n v_active_partiton_name := emt_sanitize_name(v_main_partiton_name || '_active');\n v_archived_partiton_name := emt_sanitize_name(v_main_partiton_name || '_archived');\n\n\n -- create default partition\n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I PARTITION OF %I\n FOR VALUES IN (%L) PARTITION BY LIST (is_archived);',\n v_main_partiton_name, tableName, partition_name\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I PARTITION OF %I\n FOR VALUES IN (FALSE);',\n v_active_partiton_name, v_main_partiton_name\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I PARTITION OF %I\n FOR VALUES IN (TRUE);',\n v_archived_partiton_name, v_main_partiton_name\n );\n END;\n $$ LANGUAGE plpgsql;`,\n);\n\nexport const addPartitionSQL = rawSql(\n `\n CREATE OR REPLACE FUNCTION emt_add_partition(partition_name TEXT) RETURNS void AS $$\n BEGIN \n PERFORM emt_add_table_partition('${messagesTable.name}', partition_name);\n PERFORM emt_add_table_partition('${streamsTable.name}', partition_name);\n\n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I PARTITION OF %I\n FOR VALUES IN (%L);',\n emt_sanitize_name('${subscriptionsTable.name}' || '_' || partition_name), '${subscriptionsTable.name}', partition_name\n );\n END;\n $$ LANGUAGE plpgsql;`,\n);\n\nexport const dropFutureConceptModuleAndTenantFunctions = SQL`\n DROP FUNCTION IF EXISTS add_module(TEXT);\n DROP FUNCTION IF EXISTS add_tenant(TEXT, TEXT);\n DROP FUNCTION IF EXISTS add_module_for_all_tenants(TEXT);\n DROP FUNCTION IF EXISTS add_tenant_for_all_modules(TEXT);\n`;\nexport const addModuleSQL = rawSql(\n `\n CREATE OR REPLACE FUNCTION add_module(new_module TEXT) RETURNS void AS $$\n BEGIN\n -- For ${messagesTable.name} table\n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I PARTITION OF %I\n FOR VALUES IN (emt_sanitize_name(%L || ''__'' || %L)) PARTITION BY LIST (is_archived);',\n emt_sanitize_name('${messagesTable.name}_' || new_module || '__' || '${globalTag}'), '${messagesTable.name}', new_module, '${globalTag}'\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I_active PARTITION OF %I\n FOR VALUES IN (FALSE);',\n emt_sanitize_name('${messagesTable.name}_' || new_module || '__' || '${globalTag}' || '_active'), emt_sanitize_name('${messagesTable.name}_' || new_module || '__' || '${globalTag}')\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I_archived PARTITION OF %I\n FOR VALUES IN (TRUE);',\n emt_sanitize_name('${messagesTable.name}_' || new_module || '__' || '${globalTag}' || '_archived'), emt_sanitize_name('${messagesTable.name}_' || new_module || '__' || '${globalTag}')\n );\n \n -- For ${streamsTable.name} table\n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I PARTITION OF %I\n FOR VALUES IN (emt_sanitize_name(%L || ''__'' || %L)) PARTITION BY LIST (is_archived);',\n emt_sanitize_name('${streamsTable.name}_' || new_module || '__' || '${globalTag}'), '${streamsTable.name}', new_module, '${globalTag}'\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I_active PARTITION OF %I\n FOR VALUES IN (FALSE);',\n emt_sanitize_name('${streamsTable.name}_' || new_module || '__' || '${globalTag}' || '_active'), emt_sanitize_name('${streamsTable.name}_' || new_module || '__' || '${globalTag}')\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I_archived PARTITION OF %I\n FOR VALUES IN (TRUE);',\n emt_sanitize_name('${streamsTable.name}_' || new_module || '__' || '${globalTag}' || '_archived'), emt_sanitize_name('${streamsTable.name}_' || new_module || '__' || '${globalTag}')\n );\n END;\n $$ LANGUAGE plpgsql;\n `,\n);\n\nexport const addTenantSQL = rawSql(\n `\n CREATE OR REPLACE FUNCTION add_tenant(new_module TEXT, new_tenant TEXT) RETURNS void AS $$\n BEGIN\n -- For ${messagesTable.name} table\n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I PARTITION OF %I\n FOR VALUES IN (emt_sanitize_name(''%s__%s'')) PARTITION BY LIST (is_archived);',\n emt_sanitize_name('${messagesTable.name}_' || new_module || '__' || new_tenant), '${messagesTable.name}', new_module, new_tenant\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I_active PARTITION OF %I\n FOR VALUES IN (FALSE);',\n emt_sanitize_name('${messagesTable.name}_' || new_module || '__' || new_tenant || '_active'), emt_sanitize_name('${messagesTable.name}_' || new_module || '__' || new_tenant)\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I_archived PARTITION OF %I\n FOR VALUES IN (TRUE);',\n emt_sanitize_name('${messagesTable.name}_' || new_module || '__' || new_tenant || '_archived'), emt_sanitize_name('${messagesTable.name}_' || new_module || '__' || new_tenant)\n );\n \n -- For ${streamsTable.name} table\n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I PARTITION OF %I\n FOR VALUES IN (emt_sanitize_name(''%s__%s'')) PARTITION BY LIST (is_archived);',\n emt_sanitize_name('${streamsTable.name}_' || new_module || '__' || new_tenant), '${streamsTable.name}', new_module, new_tenant\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I_active PARTITION OF %I\n FOR VALUES IN (FALSE);',\n emt_sanitize_name('${streamsTable.name}_' || new_module || '__' || new_tenant || '_active'), emt_sanitize_name('${streamsTable.name}_' || new_module || '__' || new_tenant)\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I_archived PARTITION OF %I\n FOR VALUES IN (TRUE);',\n emt_sanitize_name('${streamsTable.name}_' || new_module || '__' || new_tenant || '_archived'), emt_sanitize_name('${streamsTable.name}_' || new_module || '__' || new_tenant)\n );\n END;\n $$ LANGUAGE plpgsql;\n `,\n);\n\nexport const addModuleForAllTenantsSQL = rawSql(\n `\n CREATE OR REPLACE FUNCTION add_module_for_all_tenants(new_module TEXT) RETURNS void AS $$\n DECLARE\n tenant_record RECORD;\n BEGIN\n PERFORM add_module(new_module);\n \n FOR tenant_record IN SELECT DISTINCT tenant FROM ${messagesTable.name}\n LOOP\n -- For ${messagesTable.name} table\n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I PARTITION OF %I\n FOR VALUES IN (emt_sanitize_name(''%s__%s'')) PARTITION BY LIST (is_archived);',\n emt_sanitize_name('${messagesTable.name}_' || new_module || '__' || tenant_record.tenant), '${messagesTable.name}', new_module, tenant_record.tenant\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I_active PARTITION OF %I\n FOR VALUES IN (FALSE);',\n emt_sanitize_name('${messagesTable.name}_' || new_module || '__' || tenant_record.tenant || '_active'), emt_sanitize_name('${messagesTable.name}_' || new_module || '__' || tenant_record.tenant)\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I_archived PARTITION OF %I\n FOR VALUES IN (TRUE);',\n emt_sanitize_name('${messagesTable.name}_' || new_module || '__' || tenant_record.tenant || '_archived'), emt_sanitize_name('${messagesTable.name}_' || new_module || '__' || tenant_record.tenant)\n );\n \n -- For ${streamsTable.name} table\n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I PARTITION OF %I\n FOR VALUES IN (emt_sanitize_name(''%s__%s'')) PARTITION BY LIST (is_archived);',\n emt_sanitize_name('${streamsTable.name}_' || new_module || '__' || tenant_record.tenant), '${streamsTable.name}', new_module, tenant_record.tenant\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I_active PARTITION OF %I\n FOR VALUES IN (FALSE);',\n emt_sanitize_name('${streamsTable.name}_' || new_module || '__' || tenant_record.tenant || '_active'), emt_sanitize_name('${streamsTable.name}_' || new_module || '__' || tenant_record.tenant)\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I_archived PARTITION OF %I\n FOR VALUES IN (TRUE);',\n emt_sanitize_name('${streamsTable.name}_' || new_module || '__' || tenant_record.tenant || '_archived'), emt_sanitize_name('${streamsTable.name}_' || new_module || '__' || tenant_record.tenant)\n );\n END LOOP;\n END;\n $$ LANGUAGE plpgsql;\n `,\n);\n\nexport const addTenantForAllModulesSQL = rawSql(\n `\n CREATE OR REPLACE FUNCTION add_tenant_for_all_modules(new_tenant TEXT) RETURNS void AS $$\n DECLARE\n module_record RECORD;\n BEGIN\n FOR module_record IN SELECT DISTINCT partitionname FROM pg_partman.part_config WHERE parent_table = '${messagesTable.name}'\n LOOP\n -- For ${messagesTable.name} table\n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I PARTITION OF %I\n FOR VALUES IN (emt_sanitize_name(''%s__%s'')) PARTITION BY LIST (is_archived);',\n emt_sanitize_name('${messagesTable.name}_' || module_record.partitionname || '__' || new_tenant), '${messagesTable.name}', module_record.partitionname, new_tenant\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I_active PARTITION OF %I\n FOR VALUES IN (FALSE);',\n emt_sanitize_name('${messagesTable.name}_' || module_record.partitionname || '__' || new_tenant || '_active'), emt_sanitize_name('${messagesTable.name}_' || module_record.partitionname || '__' || new_tenant)\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I_archived PARTITION OF %I\n FOR VALUES IN (TRUE);',\n emt_sanitize_name('${messagesTable.name}_' || module_record.partitionname || '__' || new_tenant || '_archived'), emt_sanitize_name('${messagesTable.name}_' || module_record.partitionname || '__' || new_tenant)\n );\n \n -- For ${streamsTable.name} table\n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I PARTITION OF %I\n FOR VALUES IN (emt_sanitize_name(''%s__%s'')) PARTITION BY LIST (is_archived);',\n emt_sanitize_name('${streamsTable.name}_' || module_record.partitionname || '__' || new_tenant), '${streamsTable.name}', module_record.partitionname, new_tenant\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I_active PARTITION OF %I\n FOR VALUES IN (FALSE);',\n emt_sanitize_name('${streamsTable.name}_' || module_record.partitionname || '__' || new_tenant || '_active'), emt_sanitize_name('${streamsTable.name}_' || module_record.partitionname || '__' || new_tenant)\n );\n \n EXECUTE format('\n CREATE TABLE IF NOT EXISTS %I_archived PARTITION OF %I\n FOR VALUES IN (TRUE);',\n emt_sanitize_name('${streamsTable.name}_' || module_record.partitionname || '__' || new_tenant || '_archived'), emt_sanitize_name('${streamsTable.name}_' || module_record.partitionname || '__' || new_tenant)\n );\n END LOOP;\n END;\n $$ LANGUAGE plpgsql;\n `,\n);\n\nexport const addDefaultPartitionSQL = rawSql(\n `SELECT emt_add_partition('${defaultTag}');`,\n);\n\nexport const migrationFromEventsToMessagesSQL = rawSql(`\nDO $$ \nDECLARE\n partition_record RECORD;\nBEGIN\n -- Rename the main table and its columns if it exists\n IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'emt_events') THEN\n -- Rename all partitions first\n FOR partition_record IN \n SELECT tablename \n FROM pg_tables \n WHERE tablename LIKE 'emt_events_%'\n ORDER BY tablename DESC -- to handle child partitions first\n LOOP\n EXECUTE format('ALTER TABLE %I RENAME TO %I', \n partition_record.tablename, \n REPLACE(partition_record.tablename, 'events', 'messages'));\n END LOOP;\n\n -- Rename the main table\n ALTER TABLE emt_events RENAME TO emt_messages;\n \n -- Rename columns\n ALTER TABLE emt_messages \n RENAME COLUMN event_data TO message_data;\n ALTER TABLE emt_messages \n RENAME COLUMN event_metadata TO message_metadata;\n ALTER TABLE emt_messages \n RENAME COLUMN event_schema_version TO message_schema_version;\n ALTER TABLE emt_messages \n RENAME COLUMN event_type TO message_type;\n ALTER TABLE emt_messages \n RENAME COLUMN event_id TO message_id;\n ALTER TABLE emt_messages \n ADD COLUMN message_kind CHAR(1) NOT NULL DEFAULT 'E';\n\n -- Rename sequence if it exists\n IF EXISTS (SELECT 1 FROM pg_sequences WHERE sequencename = 'emt_global_event_position') THEN\n ALTER SEQUENCE emt_global_event_position \n RENAME TO emt_global_message_position;\n \n ALTER TABLE emt_messages \n ALTER COLUMN global_position \n SET DEFAULT nextval('emt_global_message_position');\n END IF;\n END IF;\nEND $$;`);\n","import { singleOrNull, sql, type SQLExecutor } from '@event-driven-io/dumbo';\nimport { defaultTag, subscriptionsTable } from './typing';\n\ntype ReadProcessorCheckpointSqlResult = {\n last_processed_position: string;\n};\n\nexport type ReadProcessorCheckpointResult = {\n lastProcessedPosition: bigint | null;\n};\n\nexport const readProcessorCheckpoint = async (\n execute: SQLExecutor,\n options: { processorId: string; partition?: string },\n): Promise<ReadProcessorCheckpointResult> => {\n const result = await singleOrNull(\n execute.query<ReadProcessorCheckpointSqlResult>(\n sql(\n `SELECT last_processed_position\n FROM ${subscriptionsTable.name}\n WHERE partition = %L AND subscription_id = %L\n LIMIT 1`,\n options?.partition ?? defaultTag,\n options.processorId,\n ),\n ),\n );\n\n return {\n lastProcessedPosition:\n result !== null ? BigInt(result.last_processed_position) : null,\n };\n};\n","import { mapRows, sql, type SQLExecutor } from '@event-driven-io/dumbo';\nimport {\n type CombinedReadEventMetadata,\n type Event,\n type EventDataOf,\n type EventMetaDataOf,\n type EventTypeOf,\n type ReadEvent,\n type ReadEventMetadataWithGlobalPosition,\n type ReadStreamOptions,\n type ReadStreamResult,\n} from '@event-driven-io/emmett';\nimport { PostgreSQLEventStoreDefaultStreamVersion } from '../postgreSQLEventStore';\nimport { defaultTag, messagesTable } from './typing';\n\ntype ReadStreamSqlResult<EventType extends Event> = {\n stream_position: string;\n message_data: EventDataOf<EventType>;\n message_metadata: EventMetaDataOf<EventType>;\n message_schema_version: string;\n message_type: EventTypeOf<EventType>;\n message_id: string;\n global_position: string;\n transaction_id: string;\n created: string;\n};\n\nexport const readStream = async <EventType extends Event>(\n execute: SQLExecutor,\n streamId: string,\n options?: ReadStreamOptions & { partition?: string },\n): Promise<\n ReadStreamResult<EventType, ReadEventMetadataWithGlobalPosition>\n> => {\n const fromCondition: string =\n options && 'from' in options\n ? `AND stream_position >= ${options.from}`\n : '';\n\n const to = Number(\n options && 'to' in options\n ? options.to\n : options && 'maxCount' in options && options.maxCount\n ? options.from + options.maxCount\n : NaN,\n );\n\n const toCondition = !isNaN(to) ? `AND stream_position <= ${to}` : '';\n\n const events: ReadEvent<EventType, ReadEventMetadataWithGlobalPosition>[] =\n await mapRows(\n execute.query<ReadStreamSqlResult<EventType>>(\n sql(\n `SELECT stream_id, stream_position, global_position, message_data, message_metadata, message_schema_version, message_type, message_id\n FROM ${messagesTable.name}\n WHERE stream_id = %L AND partition = %L AND is_archived = FALSE ${fromCondition} ${toCondition}\n ORDER BY stream_position ASC`,\n streamId,\n options?.partition ?? defaultTag,\n ),\n ),\n (row) => {\n const rawEvent = {\n type: row.message_type,\n data: row.message_data,\n metadata: row.message_metadata,\n } as unknown as EventType;\n\n const metadata: ReadEventMetadataWithGlobalPosition = {\n ...('metadata' in rawEvent ? (rawEvent.metadata ?? {}) : {}),\n messageId: row.message_id,\n streamName: streamId,\n streamPosition: BigInt(row.stream_position),\n globalPosition: BigInt(row.global_position),\n };\n\n return {\n ...rawEvent,\n kind: 'Event',\n metadata: metadata as CombinedReadEventMetadata<\n EventType,\n ReadEventMetadataWithGlobalPosition\n >,\n };\n },\n );\n\n return events.length > 0\n ? {\n currentStreamVersion:\n events[events.length - 1]!.metadata.streamPosition,\n events,\n streamExists: true,\n }\n : {\n currentStreamVersion: PostgreSQLEventStoreDefaultStreamVersion,\n events: [],\n streamExists: false,\n };\n};\n","import type {\n AnyEvent,\n ProjectorOptions,\n ReadEventMetadataWithGlobalPosition,\n} from '@event-driven-io/emmett';\nimport { v7 as uuid } from 'uuid';\nimport type { PostgreSQLProjectionDefinition } from '../projections';\nimport {\n postgreSQLEventStoreConsumer,\n type PostgreSQLEventStoreConsumer,\n type PostgreSQLEventStoreConsumerOptions,\n} from './postgreSQLEventStoreConsumer';\nimport type { PostgreSQLProcessorHandlerContext } from './postgreSQLProcessor';\n\nexport const rebuildPostgreSQLProjections = <\n EventType extends AnyEvent = AnyEvent,\n>(\n options: Omit<\n PostgreSQLEventStoreConsumerOptions<EventType>,\n 'stopWhen' | 'processors'\n > &\n (\n | {\n projections: (\n | ProjectorOptions<\n EventType,\n ReadEventMetadataWithGlobalPosition,\n PostgreSQLProcessorHandlerContext\n >\n | PostgreSQLProjectionDefinition<EventType>\n )[];\n }\n | ProjectorOptions<\n EventType,\n ReadEventMetadataWithGlobalPosition,\n PostgreSQLProcessorHandlerContext\n >\n ),\n): PostgreSQLEventStoreConsumer<EventType> => {\n const consumer = postgreSQLEventStoreConsumer({\n ...options,\n stopWhen: { noMessagesLeft: true },\n });\n\n const projections: (Omit<\n ProjectorOptions<\n EventType,\n ReadEventMetadataWithGlobalPosition,\n PostgreSQLProcessorHandlerContext\n >,\n 'processorId'\n > & { processorId?: string })[] =\n 'projections' in options\n ? options.projections.map((p) =>\n 'projection' in p\n ? {\n ...p,\n processorId: `projection:${p.projection.name ?? uuid()}-rebuild`,\n truncateOnStart: p.truncateOnStart ?? true,\n }\n : {\n projection: p,\n processorId: `projection:${p.name ?? uuid()}-rebuild`,\n truncateOnStart: true,\n },\n )\n : [options];\n\n for (const projectionDefinition of projections) {\n consumer.projector({\n ...projectionDefinition,\n processorId:\n projectionDefinition.processorId ??\n `projection:${projectionDefinition.projection.name ?? uuid()}-rebuild`,\n truncateOnStart: projectionDefinition.truncateOnStart ?? true,\n });\n }\n\n return consumer;\n};\n","import {\n type CanHandle,\n type Event,\n type ReadEvent,\n type TruncateProjection,\n} from '@event-driven-io/emmett';\nimport {\n pongoClient,\n type PongoClient,\n type PongoDocument,\n} from '@event-driven-io/pongo';\nimport {\n postgreSQLProjection,\n type PostgreSQLProjectionDefinition,\n type PostgreSQLProjectionHandlerContext,\n} from '..';\nimport type { PostgresReadEventMetadata } from '../../postgreSQLEventStore';\n\nexport type PongoProjectionHandlerContext =\n PostgreSQLProjectionHandlerContext & {\n pongo: PongoClient;\n };\n\nexport type PongoWithNotNullDocumentEvolve<\n Document extends PongoDocument,\n EventType extends Event,\n EventMetaDataType extends\n PostgresReadEventMetadata = PostgresReadEventMetadata,\n> =\n | ((\n document: Document,\n event: ReadEvent<EventType, EventMetaDataType>,\n ) => Document | null)\n | ((\n document: Document,\n event: ReadEvent<EventType>,\n ) => Promise<Document | null>);\n\nexport type PongoWithNullableDocumentEvolve<\n Document extends PongoDocument,\n EventType extends Event,\n EventMetaDataType extends\n PostgresReadEventMetadata = PostgresReadEventMetadata,\n> =\n | ((\n document: Document | null,\n event: ReadEvent<EventType, EventMetaDataType>,\n ) => Document | null)\n | ((\n document: Document | null,\n event: ReadEvent<EventType>,\n ) => Promise<Document | null>);\n\nexport type PongoDocumentEvolve<\n Document extends PongoDocument,\n EventType extends Event,\n EventMetaDataType extends\n PostgresReadEventMetadata = PostgresReadEventMetadata,\n> =\n | PongoWithNotNullDocumentEvolve<Document, EventType, EventMetaDataType>\n | PongoWithNullableDocumentEvolve<Document, EventType, EventMetaDataType>;\n\nexport type PongoProjectionOptions<EventType extends Event> = {\n handle: (\n events: ReadEvent<EventType, PostgresReadEventMetadata>[],\n context: PongoProjectionHandlerContext,\n ) => Promise<void>;\n canHandle: CanHandle<EventType>;\n truncate?: TruncateProjection<PongoProjectionHandlerContext>;\n};\n\nexport const pongoProjection = <EventType extends Event>({\n truncate,\n handle,\n canHandle,\n}: PongoProjectionOptions<EventType>): PostgreSQLProjectionDefinition<EventType> =>\n postgreSQLProjection<EventType>({\n canHandle,\n handle: async (events, context) => {\n const {\n connection: { connectionString, client, pool },\n } = context;\n const pongo = pongoClient(connectionString, {\n connectionOptions: { client, pool },\n });\n await handle(events, {\n ...context,\n pongo,\n });\n },\n truncate: truncate\n ? (context) => {\n const {\n connection: { connectionString, client, pool },\n } = context;\n const pongo = pongoClient(connectionString, {\n connectionOptions: { client, pool },\n });\n return truncate({\n ...context,\n pongo,\n });\n }\n : undefined,\n });\n\nexport type PongoMultiStreamProjectionOptions<\n Document extends PongoDocument,\n EventType extends Event,\n EventMetaDataType extends\n PostgresReadEventMetadata = PostgresReadEventMetadata,\n> = {\n canHandle: CanHandle<EventType>;\n\n collectionName: string;\n getDocumentId: (event: ReadEvent<EventType>) => string;\n} & (\n | {\n evolve: PongoWithNullableDocumentEvolve<\n Document,\n EventType,\n EventMetaDataType\n >;\n }\n | {\n evolve: PongoWithNotNullDocumentEvolve<\n Document,\n EventType,\n EventMetaDataType\n >;\n initialState: () => Document;\n }\n);\n\nexport const pongoMultiStreamProjection = <\n Document extends PongoDocument,\n EventType extends Event,\n EventMetaDataType extends\n PostgresReadEventMetadata = PostgresReadEventMetadata,\n>(\n options: PongoMultiStreamProjectionOptions<\n Document,\n EventType,\n EventMetaDataType\n >,\n): PostgreSQLProjectionDefinition<EventType> => {\n const { collectionName, getDocumentId, canHandle } = options;\n\n return pongoProjection({\n handle: async (events, { pongo }) => {\n const collection = pongo.db().collection<Document>(collectionName);\n\n for (const event of events) {\n await collection.handle(getDocumentId(event), async (document) => {\n return 'initialState' in options\n ? await options.evolve(\n document ?? options.initialState(),\n event as ReadEvent<EventType, EventMetaDataType>,\n )\n : await options.evolve(\n document,\n event as ReadEvent<EventType, EventMetaDataType>,\n );\n });\n }\n },\n canHandle,\n truncate: async (context) => {\n const {\n connection: { connectionString, client, pool },\n } = context;\n const pongo = pongoClient(connectionString, {\n connectionOptions: { client, pool },\n });\n\n await pongo.db().collection<Document>(collectionName).deleteMany();\n },\n });\n};\n\nexport type PongoSingleStreamProjectionOptions<\n Document extends PongoDocument,\n EventType extends Event,\n EventMetaDataType extends\n PostgresReadEventMetadata = PostgresReadEventMetadata,\n> = {\n canHandle: CanHandle<EventType>;\n getDocumentId?: (event: ReadEvent<EventType>) => string;\n\n collectionName: string;\n} & (\n | {\n evolve: PongoWithNullableDocumentEvolve<\n Document,\n EventType,\n EventMetaDataType\n >;\n }\n | {\n evolve: PongoWithNotNullDocumentEvolve<\n Document,\n EventType,\n EventMetaDataType\n >;\n initialState: () => Document;\n }\n);\n\nexport const pongoSingleStreamProjection = <\n Document extends PongoDocument,\n EventType extends Event,\n EventMetaDataType extends\n PostgresReadEventMetadata = PostgresReadEventMetadata,\n>(\n options: PongoSingleStreamProjectionOptions<\n Document,\n EventType,\n EventMetaDataType\n >,\n): PostgreSQLProjectionDefinition<EventType> => {\n return pongoMultiStreamProjection<Document, EventType, EventMetaDataType>({\n ...options,\n getDocumentId:\n options.getDocumentId ?? ((event) => event.metadata.streamName),\n });\n};\n","import { type Dumbo } from '@event-driven-io/dumbo';\nimport {\n assertDeepEqual,\n assertEqual,\n assertIsNotNull,\n assertThatArray,\n} from '@event-driven-io/emmett';\nimport {\n pongoClient,\n type PongoCollection,\n type PongoDocument,\n type PongoFilter,\n type WithId,\n} from '@event-driven-io/pongo';\nimport { type PostgreSQLProjectionAssert } from '..';\n\nexport type PongoAssertOptions = {\n inCollection: string;\n inDatabase?: string;\n};\n\nconst withCollection = (\n handle: (collection: PongoCollection<PongoDocument>) => Promise<void>,\n options: {\n pool: Dumbo;\n connectionString: string;\n } & PongoAssertOptions,\n) => {\n const { pool, connectionString, inDatabase, inCollection } = options;\n\n return pool.withConnection(async (connection) => {\n const pongo = pongoClient(connectionString, {\n connectionOptions: { connection },\n });\n try {\n const collection = pongo.db(inDatabase).collection(inCollection);\n\n return handle(collection);\n } finally {\n await pongo.close();\n }\n });\n};\n\nconst withoutIdAndVersion = <Doc extends PongoDocument | WithId<PongoDocument>>(\n doc: Doc,\n) => {\n const { _id, _version, ...without } = doc;\n\n return without;\n};\n\nconst assertDocumentsEqual = <\n Doc extends PongoDocument | WithId<PongoDocument>,\n>(\n actual: PongoDocument,\n expected: Doc,\n) => {\n if ('_id' in expected)\n assertEqual(\n expected._id,\n actual._id,\n // eslint-disable-next-line @typescript-eslint/restrict-template-expressions\n `Document ids are not matching! Expected: ${expected._id}, Actual: ${actual._id}`,\n );\n\n return assertDeepEqual(\n withoutIdAndVersion(actual),\n withoutIdAndVersion(expected),\n );\n};\n\ntype FilterOrId<Doc extends PongoDocument | WithId<PongoDocument>> =\n | { withId: string }\n | {\n matchingFilter: PongoFilter<Doc>;\n };\n\nexport const documentExists =\n <Doc extends PongoDocument | WithId<PongoDocument>>(\n document: Doc,\n options: PongoAssertOptions & FilterOrId<Doc>,\n ): PostgreSQLProjectionAssert =>\n (assertOptions) =>\n withCollection(\n async (collection) => {\n const result = await collection.findOne(\n 'withId' in options\n ? { _id: options.withId }\n : options.matchingFilter,\n );\n\n assertIsNotNull(result);\n\n assertDocumentsEqual(result, document);\n },\n { ...options, ...assertOptions },\n );\n\nexport const documentsAreTheSame =\n <Doc extends PongoDocument | WithId<PongoDocument>>(\n documents: Doc[],\n options: PongoAssertOptions & FilterOrId<Doc>,\n ): PostgreSQLProjectionAssert =>\n (assertOptions) =>\n withCollection(\n async (collection) => {\n const result = await collection.find(\n 'withId' in options\n ? { _id: options.withId }\n : options.matchingFilter,\n );\n\n assertEqual(\n documents.length,\n result.length,\n 'Different Documents Count than expected',\n );\n\n for (let i = 0; i < documents.length; i++) {\n assertThatArray(result as Doc[]).contains(documents[i]!);\n }\n },\n { ...options, ...assertOptions },\n );\n\nexport const documentsMatchingHaveCount =\n <Doc extends PongoDocument | WithId<PongoDocument>>(\n expectedCount: number,\n options: PongoAssertOptions & FilterOrId<Doc>,\n ): PostgreSQLProjectionAssert =>\n (assertOptions) =>\n withCollection(\n async (collection) => {\n const result = await collection.find(\n 'withId' in options\n ? { _id: options.withId }\n : options.matchingFilter,\n );\n\n assertEqual(\n expectedCount,\n result.length,\n 'Different Documents Count than expected',\n );\n },\n { ...options, ...assertOptions },\n );\n\nexport const documentMatchingExists =\n <Doc extends PongoDocument | WithId<PongoDocument>>(\n options: PongoAssertOptions & FilterOrId<Doc>,\n ): PostgreSQLProjectionAssert =>\n (assertOptions) =>\n withCollection(\n async (collection) => {\n const result = await collection.find(\n 'withId' in options\n ? { _id: options.withId }\n : options.matchingFilter,\n );\n\n assertThatArray(result).isNotEmpty();\n },\n { ...options, ...assertOptions },\n );\n\nexport const documentDoesNotExist =\n <Doc extends PongoDocument | WithId<PongoDocument>>(\n options: PongoAssertOptions & FilterOrId<Doc>,\n ): PostgreSQLProjectionAssert =>\n (assertOptions) =>\n withCollection(\n async (collection) => {\n const result = await collection.findOne(\n 'withId' in options\n ? { _id: options.withId }\n : options.matchingFilter,\n );\n\n assertIsNotNull(result);\n },\n { ...options, ...assertOptions },\n );\n\nexport const expectPongoDocuments = {\n fromCollection: <Doc extends PongoDocument | WithId<PongoDocument>>(\n collectionName: string,\n ) => {\n return {\n withId: (id: string) => {\n return {\n toBeEqual: (document: Doc) =>\n documentExists(document, {\n withId: id,\n inCollection: collectionName,\n }),\n toExist: () =>\n documentMatchingExists({\n withId: id,\n inCollection: collectionName,\n }),\n notToExist: () =>\n documentDoesNotExist({\n withId: id,\n inCollection: collectionName,\n }),\n };\n },\n matching: <Doc extends PongoDocument | WithId<PongoDocument>>(\n filter: PongoFilter<Doc>,\n ) => {\n return {\n toBeTheSame: (documents: Doc[]) =>\n documentsAreTheSame<Doc>(documents, {\n matchingFilter: filter,\n inCollection: collectionName,\n }),\n toHaveCount: (expectedCount: number) =>\n documentsMatchingHaveCount(expectedCount, {\n matchingFilter: filter,\n inCollection: collectionName,\n }),\n toExist: () =>\n documentMatchingExists({\n matchingFilter: filter,\n inCollection: collectionName,\n }),\n notToExist: () =>\n documentDoesNotExist({\n matchingFilter: filter,\n inCollection: collectionName,\n }),\n };\n },\n };\n },\n};\n","import {\n dumbo,\n type Dumbo,\n type DumboOptions,\n type QueryResultRow,\n type SQL,\n} from '@event-driven-io/dumbo';\nimport {\n assertFails,\n AssertionError,\n assertThatArray,\n assertTrue,\n isErrorConstructor,\n type CombinedReadEventMetadata,\n type Event,\n type ReadEvent,\n type ThenThrows,\n} from '@event-driven-io/emmett';\nimport { v4 as uuid } from 'uuid';\nimport { handleProjections, type PostgreSQLProjectionDefinition } from '.';\nimport type { PostgresReadEventMetadata } from '../postgreSQLEventStore';\n\nexport type PostgreSQLProjectionSpecEvent<\n EventType extends Event,\n EventMetaDataType extends\n PostgresReadEventMetadata = PostgresReadEventMetadata,\n> = EventType & {\n metadata?: Partial<EventMetaDataType>;\n};\n\nexport type PostgreSQLProjectionSpecWhenOptions = { numberOfTimes: number };\n\nexport type PostgreSQLProjectionSpec<EventType extends Event> = (\n givenEvents: PostgreSQLProjectionSpecEvent<EventType>[],\n) => {\n when: (\n events: PostgreSQLProjectionSpecEvent<EventType>[],\n options?: PostgreSQLProjectionSpecWhenOptions,\n ) => {\n then: (\n assert: PostgreSQLProjectionAssert,\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 PostgreSQLProjectionAssert = (options: {\n pool: Dumbo;\n connectionString: string;\n}) => Promise<void | boolean>;\n\nexport type PostgreSQLProjectionSpecOptions<EventType extends Event> = {\n projection: PostgreSQLProjectionDefinition<EventType>;\n} & DumboOptions;\n\nexport const PostgreSQLProjectionSpec = {\n for: <EventType extends Event>(\n options: PostgreSQLProjectionSpecOptions<EventType>,\n ): PostgreSQLProjectionSpec<EventType> => {\n {\n const { projection, ...dumoOptions } = options;\n const { connectionString } = dumoOptions;\n\n return (givenEvents: PostgreSQLProjectionSpecEvent<EventType>[]) => {\n return {\n when: (\n events: PostgreSQLProjectionSpecEvent<EventType>[],\n options?: PostgreSQLProjectionSpecWhenOptions,\n ) => {\n const allEvents: ReadEvent<EventType, PostgresReadEventMetadata>[] =\n [];\n\n const run = async (pool: Dumbo) => {\n let globalPosition = 0n;\n const numberOfTimes = options?.numberOfTimes ?? 1;\n\n for (const event of [\n ...givenEvents,\n ...Array.from({ length: numberOfTimes }).flatMap(() => events),\n ]) {\n const metadata: PostgresReadEventMetadata = {\n globalPosition: ++globalPosition,\n streamPosition: globalPosition,\n streamName: `test-${uuid()}`,\n messageId: uuid(),\n };\n\n allEvents.push({\n ...event,\n kind: 'Event',\n metadata: {\n ...metadata,\n ...('metadata' in event ? (event.metadata ?? {}) : {}),\n } as CombinedReadEventMetadata<\n EventType,\n PostgresReadEventMetadata\n >,\n });\n }\n\n await pool.withTransaction((transaction) =>\n handleProjections({\n events: allEvents,\n projections: [projection],\n connection: {\n pool,\n connectionString,\n transaction,\n },\n }),\n );\n };\n\n return {\n then: async (\n assert: PostgreSQLProjectionAssert,\n message?: string,\n ): Promise<void> => {\n const pool = dumbo(dumoOptions);\n try {\n await run(pool);\n\n const succeeded = await assert({ pool, connectionString });\n\n if (succeeded !== undefined && succeeded === false)\n assertFails(\n message ??\n \"Projection specification didn't match the criteria\",\n );\n } finally {\n await pool.close();\n }\n },\n thenThrows: async <ErrorType extends Error>(\n ...args: Parameters<ThenThrows<ErrorType>>\n ): Promise<void> => {\n const pool = dumbo(dumoOptions);\n try {\n await run(pool);\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 pool.close();\n }\n },\n };\n },\n };\n };\n }\n },\n};\n\nexport const eventInStream = <\n EventType extends Event = Event,\n EventMetaDataType extends\n PostgresReadEventMetadata = PostgresReadEventMetadata,\n>(\n streamName: string,\n event: PostgreSQLProjectionSpecEvent<EventType, EventMetaDataType>,\n): PostgreSQLProjectionSpecEvent<EventType, EventMetaDataType> => {\n return {\n ...event,\n metadata: {\n ...(event.metadata ?? {}),\n streamName: event.metadata?.streamName ?? streamName,\n } as Partial<EventMetaDataType>,\n };\n};\n\nexport const eventsInStream = <\n EventType extends Event = Event,\n EventMetaDataType extends\n PostgresReadEventMetadata = PostgresReadEventMetadata,\n>(\n streamName: string,\n events: PostgreSQLProjectionSpecEvent<EventType, EventMetaDataType>[],\n): PostgreSQLProjectionSpecEvent<EventType, EventMetaDataType>[] => {\n return events.map((e) => eventInStream(streamName, e));\n};\n\nexport const newEventsInStream = eventsInStream;\n\nexport const assertSQLQueryResultMatches =\n <T extends QueryResultRow>(sql: SQL, rows: T[]): PostgreSQLProjectionAssert =>\n async ({ pool: { execute } }) => {\n const result = await execute.query<T>(sql);\n\n assertThatArray(rows).containsExactlyInAnyOrder(result.rows);\n };\n\nexport const expectSQL = {\n query: (sql: SQL) => ({\n resultRows: {\n toBeTheSame: <T extends QueryResultRow>(rows: T[]) =>\n assertSQLQueryResultMatches(sql, rows),\n },\n }),\n};\n","import {\n type Dumbo,\n type NodePostgresClient,\n type NodePostgresTransaction,\n type SQL,\n type SQLExecutor,\n} from '@event-driven-io/dumbo';\nimport {\n projection,\n type CanHandle,\n type Event,\n type ProjectionDefinition,\n type ProjectionHandler,\n type ReadEvent,\n} from '@event-driven-io/emmett';\nimport type { PostgresReadEventMetadata } from '../postgreSQLEventStore';\n\nexport type PostgreSQLProjectionHandlerContext = {\n execute: SQLExecutor;\n connection: {\n connectionString: string;\n client: NodePostgresClient;\n transaction: NodePostgresTransaction;\n pool: Dumbo;\n };\n};\n\nexport type PostgreSQLProjectionHandler<\n EventType extends Event = Event,\n EventMetaDataType extends\n PostgresReadEventMetadata = PostgresReadEventMetadata,\n> = ProjectionHandler<\n EventType,\n EventMetaDataType,\n PostgreSQLProjectionHandlerContext\n>;\n\nexport type PostgreSQLProjectionDefinition<EventType extends Event = Event> =\n ProjectionDefinition<\n EventType,\n PostgresReadEventMetadata,\n PostgreSQLProjectionHandlerContext\n >;\n\nexport type PostgreSQLProjectionHandlerOptions<\n EventType extends Event = Event,\n> = {\n events: ReadEvent<EventType, PostgresReadEventMetadata>[];\n projections: PostgreSQLProjectionDefinition<EventType>[];\n connection: {\n connectionString: string;\n transaction: NodePostgresTransaction;\n pool: Dumbo;\n };\n};\n\nexport const handleProjections = async <EventType extends Event = Event>(\n options: PostgreSQLProjectionHandlerOptions<EventType>,\n): Promise<void> => {\n const {\n projections: allProjections,\n events,\n connection: { pool, transaction, connectionString },\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 const client = (await transaction.connection.open()) as NodePostgresClient;\n\n for (const projection of projections) {\n await projection.handle(events, {\n connection: {\n connectionString,\n pool,\n client,\n transaction,\n },\n execute: transaction.execute,\n });\n }\n};\n\nexport const postgreSQLProjection = <EventType extends Event>(\n definition: PostgreSQLProjectionDefinition<EventType>,\n): PostgreSQLProjectionDefinition<EventType> =>\n projection<\n EventType,\n PostgresReadEventMetadata,\n PostgreSQLProjectionHandlerContext\n >(definition);\n\nexport const postgreSQLRawBatchSQLProjection = <EventType extends Event>(\n handle: (\n events: EventType[],\n context: PostgreSQLProjectionHandlerContext,\n ) => Promise<SQL[]> | SQL[],\n ...canHandle: CanHandle<EventType>\n): PostgreSQLProjectionDefinition<EventType> =>\n postgreSQLProjection<EventType>({\n canHandle,\n handle: async (events, context) => {\n const sqls: SQL[] = await handle(events, context);\n\n await context.execute.batchCommand(sqls);\n },\n });\n\nexport const postgreSQLRawSQLProjection = <EventType extends Event>(\n handle: (\n event: EventType,\n context: PostgreSQLProjectionHandlerContext,\n ) => Promise<SQL> | SQL,\n ...canHandle: CanHandle<EventType>\n): PostgreSQLProjectionDefinition<EventType> =>\n postgreSQLRawBatchSQLProjection<EventType>(\n async (events, context) => {\n const sqls: SQL[] = [];\n\n for (const event of events) {\n sqls.push(await handle(event, context));\n }\n return sqls;\n },\n ...canHandle,\n );\n"]}