@codeforbreakfast/eventsourcing-store-inmemory
Version:
In-memory event store implementation for development and testing - Fast in-memory storage with complete EventStore interface implementation
68 lines (57 loc) • 2.38 kB
text/typescript
import { Effect, Scope, Sink, Stream, pipe } from 'effect';
import {
EventStreamPosition,
EventStore,
eventStoreError,
EventStoreError,
} from '@codeforbreakfast/eventsourcing-store';
import { type InMemoryStore } from './InMemoryStore';
export interface SubscribableEventStore<T> extends EventStore<T> {
readonly subscribeToStream: (
streamId: EventStreamPosition['streamId']
) => Effect.Effect<Stream.Stream<T, never>, EventStoreError, Scope.Scope>;
}
const dropEventsFromStream =
<T>(count: number) =>
(stream: Readonly<Stream.Stream<T, never, never>>) =>
Stream.drop(stream, count);
const readHistoricalEvents =
<T>(store: InMemoryStore<T>) =>
(from: EventStreamPosition) =>
pipe(from.streamId, store.getHistorical, Effect.map(dropEventsFromStream<T>(from.eventNumber)));
const readAllEvents =
<T>(store: InMemoryStore<T>) =>
(from: EventStreamPosition) =>
pipe(from.streamId, store.get, Effect.map(dropEventsFromStream<T>(from.eventNumber)));
const createSubscribeError = (streamId: EventStreamPosition['streamId']) =>
eventStoreError.subscribe(streamId, `Failed to subscribe to stream: ${String(streamId)}`);
const subscribeToStreamWithError =
<T>(streamId: EventStreamPosition['streamId']) =>
(store: InMemoryStore<T>) =>
pipe(streamId, store.get, Effect.mapError(createSubscribeError(streamId)));
const subscribeToAllStreams = <T>(store: InMemoryStore<T>) => store.getAllLiveOnly();
export const makeInMemoryEventStore = <T>(
store: InMemoryStore<T>
): Effect.Effect<EventStore<T>, never, never> =>
Effect.succeed({
append: (to: EventStreamPosition) =>
Sink.foldChunksEffect(
to,
() => true,
(end, chunk) => pipe(chunk, store.append(end))
),
read: readHistoricalEvents(store),
subscribe: readAllEvents(store),
subscribeAll: () => subscribeToAllStreams(store),
});
const addSubscribeMethod =
<T>(store: InMemoryStore<T>) =>
(baseStore: EventStore<T>): SubscribableEventStore<T> => ({
...baseStore,
subscribeToStream: (streamId: EventStreamPosition['streamId']) =>
pipe(store, subscribeToStreamWithError(streamId)),
});
export const makeSubscribableInMemoryEventStore = <T>(
store: InMemoryStore<T>
): Effect.Effect<SubscribableEventStore<T>, never, never> =>
pipe(store, makeInMemoryEventStore, Effect.map(addSubscribeMethod(store)));