import { CommandId, EventId, IsoDateTime, NonNegativeInt, OrchestrationActorKind, OrchestrationAggregateKind, OrchestrationEvent, OrchestrationEventMetadata, OrchestrationEventType, ProjectId, ThreadId,} from "@t3tools/contracts";import * as SqlClient from "effect/unstable/sql/SqlClient";import * as SqlSchema from "effect/unstable/sql/SqlSchema";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import * as Option from "effect/Option";import * as Schema from "effect/Schema";import * as Stream from "effect/Stream"; import { toPersistenceDecodeError, toPersistenceSqlError, type OrchestrationEventStoreError,} from "../Errors.ts";import { OrchestrationEventStore, type OrchestrationEventStoreShape,} from "../Services/OrchestrationEventStore.ts"; const decodeEvent = Schema.decodeUnknownEffect(OrchestrationEvent);const UnknownFromJsonString = Schema.fromJsonString(Schema.Unknown);const EventMetadataFromJsonString = Schema.fromJsonString(OrchestrationEventMetadata); const AppendEventRequestSchema = Schema.Struct({ eventId: EventId, aggregateKind: OrchestrationAggregateKind, streamId: Schema.Union([ProjectId, ThreadId]), type: OrchestrationEventType, causationEventId: Schema.NullOr(EventId), correlationId: Schema.NullOr(CommandId), actorKind: OrchestrationActorKind, occurredAt: IsoDateTime, commandId: Schema.NullOr(CommandId), payloadJson: UnknownFromJsonString, metadataJson: EventMetadataFromJsonString,}); const OrchestrationEventPersistedRowSchema = Schema.Struct({ sequence: NonNegativeInt, eventId: EventId, type: OrchestrationEventType, aggregateKind: OrchestrationAggregateKind, aggregateId: Schema.Union([ProjectId, ThreadId]), occurredAt: IsoDateTime, commandId: Schema.NullOr(CommandId), causationEventId: Schema.NullOr(EventId), correlationId: Schema.NullOr(CommandId), payload: UnknownFromJsonString, metadata: EventMetadataFromJsonString,}); const HasEventAfterRequestSchema = Schema.Struct({ aggregateKind: Schema.String, aggregateId: Schema.String, type: Schema.optional(Schema.String), sequenceExclusive: NonNegativeInt,}); const ReadFromSequenceRequestSchema = Schema.Struct({ sequenceExclusive: NonNegativeInt, limit: Schema.Number,});const AggregateReplayRequestSchema = Schema.Struct({ aggregateKind: OrchestrationAggregateKind, aggregateId: Schema.String, fromSequenceExclusive: NonNegativeInt, toSequenceInclusive: NonNegativeInt, limit: Schema.Number,});const AggregateReplayStatsRowSchema = Schema.Struct({ eventCount: Schema.Number, payloadBytes: Schema.Number, hasCreateEvent: Schema.Number,});const DEFAULT_READ_FROM_SEQUENCE_LIMIT = 1_000;const READ_PAGE_SIZE = 500; function inferActorKind( event: Omit<OrchestrationEvent, "sequence">,): Schema.Schema.Type<typeof OrchestrationActorKind> { if (event.commandId !== null && event.commandId.startsWith("provider:")) { return "provider"; } if (event.commandId !== null && event.commandId.startsWith("server:")) { return "server"; } if ( event.metadata.providerTurnId !== undefined || event.metadata.providerItemId !== undefined || event.metadata.adapterKey !== undefined ) { return "provider"; } if (event.commandId === null) { return "server"; } return "client";} function toPersistenceSqlOrDecodeError(sqlOperation: string, decodeOperation: string) { return (cause: unknown): OrchestrationEventStoreError => Schema.isSchemaError(cause) ? toPersistenceDecodeError(decodeOperation)(cause) : toPersistenceSqlError(sqlOperation)(cause);} const makeEventStore = Effect.gen(function* () { const sql = yield* SqlClient.SqlClient; const appendEventRow = SqlSchema.findOne({ Request: AppendEventRequestSchema, Result: OrchestrationEventPersistedRowSchema, execute: (request) => sql` INSERT INTO orchestration_events ( event_id, aggregate_kind, stream_id, stream_version, event_type, occurred_at, command_id, causation_event_id, correlation_id, actor_kind, payload_json, metadata_json ) VALUES ( ${request.eventId}, ${request.aggregateKind}, ${request.streamId}, COALESCE( ( SELECT stream_version + 1 FROM orchestration_events WHERE aggregate_kind = ${request.aggregateKind} AND stream_id = ${request.streamId} ORDER BY stream_version DESC LIMIT 1 ), 0 ), ${request.type}, ${request.occurredAt}, ${request.commandId}, ${request.causationEventId}, ${request.correlationId}, ${request.actorKind}, ${request.payloadJson}, ${request.metadataJson} ) RETURNING sequence, event_id AS "eventId", event_type AS "type", aggregate_kind AS "aggregateKind", stream_id AS "aggregateId", occurred_at AS "occurredAt", command_id AS "commandId", causation_event_id AS "causationEventId", correlation_id AS "correlationId", payload_json AS "payload", metadata_json AS "metadata" `, }); const readEventRowsFromSequence = SqlSchema.findAll({ Request: ReadFromSequenceRequestSchema, Result: OrchestrationEventPersistedRowSchema, execute: (request) => sql` SELECT sequence, event_id AS "eventId", event_type AS "type", aggregate_kind AS "aggregateKind", stream_id AS "aggregateId", occurred_at AS "occurredAt", command_id AS "commandId", causation_event_id AS "causationEventId", correlation_id AS "correlationId", payload_json AS "payload", metadata_json AS "metadata" FROM orchestration_events WHERE sequence > ${request.sequenceExclusive} ORDER BY sequence ASC LIMIT ${request.limit} `, }); const readAggregateEventRows = SqlSchema.findAll({ Request: AggregateReplayRequestSchema, Result: OrchestrationEventPersistedRowSchema, execute: (request) => sql` SELECT sequence, event_id AS "eventId", event_type AS "type", aggregate_kind AS "aggregateKind", stream_id AS "aggregateId", occurred_at AS "occurredAt", command_id AS "commandId", causation_event_id AS "causationEventId", correlation_id AS "correlationId", payload_json AS "payload", metadata_json AS "metadata" FROM orchestration_events WHERE aggregate_kind = ${request.aggregateKind} AND stream_id = ${request.aggregateId} AND sequence > ${request.fromSequenceExclusive} AND sequence <= ${request.toSequenceInclusive} ORDER BY sequence ASC LIMIT ${request.limit} `, }); const readAggregateReplayStats = SqlSchema.findOne({ Request: AggregateReplayRequestSchema, Result: AggregateReplayStatsRowSchema, execute: (request) => sql` SELECT COUNT(*) AS "eventCount", COALESCE(SUM(octet_length(payload_json)), 0) AS "payloadBytes", COALESCE(MAX(event_type IN ( 'thread.created', 'project.created' )), 0) AS "hasCreateEvent" FROM ( SELECT payload_json, event_type FROM orchestration_events WHERE aggregate_kind = ${request.aggregateKind} AND stream_id = ${request.aggregateId} AND sequence > ${request.fromSequenceExclusive} AND sequence <= ${request.toSequenceInclusive} ORDER BY sequence ASC LIMIT ${request.limit} ) `, }); const append: OrchestrationEventStoreShape["append"] = (event) => appendEventRow({ eventId: event.eventId, aggregateKind: event.aggregateKind, streamId: event.aggregateId, type: event.type, causationEventId: event.causationEventId, correlationId: event.correlationId, actorKind: inferActorKind(event), occurredAt: event.occurredAt, commandId: event.commandId, payloadJson: event.payload, metadataJson: event.metadata, }).pipe( Effect.mapError( toPersistenceSqlOrDecodeError( "OrchestrationEventStore.append:insert", "OrchestrationEventStore.append:decodeRow", ), ), Effect.flatMap((row) => decodeEvent(row).pipe( Effect.mapError(toPersistenceDecodeError("OrchestrationEventStore.append:rowToEvent")), ), ), ); const readFromSequence: OrchestrationEventStoreShape["readFromSequence"] = ( sequenceExclusive, limit = DEFAULT_READ_FROM_SEQUENCE_LIMIT, ) => { const normalizedLimit = Math.max(0, Math.floor(limit)); if (normalizedLimit === 0) { return Stream.empty; } return Stream.paginate( { cursor: sequenceExclusive, remaining: normalizedLimit }, ({ cursor, remaining }) => readEventRowsFromSequence({ sequenceExclusive: cursor, limit: Math.min(remaining, READ_PAGE_SIZE), }).pipe( Effect.mapError( toPersistenceSqlOrDecodeError( "OrchestrationEventStore.readFromSequence:query", "OrchestrationEventStore.readFromSequence:decodeRows", ), ), Effect.flatMap((rows) => Effect.forEach(rows, (row) => decodeEvent(row).pipe( Effect.mapError( toPersistenceDecodeError("OrchestrationEventStore.readFromSequence:rowToEvent"), ), ), ), ), Effect.map((events) => { const last = events.at(-1); const nextRemaining = remaining - events.length; return [ events, last === undefined || nextRemaining <= 0 ? Option.none() : Option.some({ cursor: last.sequence, remaining: nextRemaining }), ] as const; }), ), ); }; const findEventAfter = SqlSchema.findOneOption({ Request: HasEventAfterRequestSchema, Result: Schema.Struct({ sequence: Schema.Number }), execute: (request) => sql` SELECT sequence FROM orchestration_events WHERE aggregate_kind = ${request.aggregateKind} AND stream_id = ${request.aggregateId} AND ${sql.and([ sql`sequence > ${request.sequenceExclusive}`, ...(request.type === undefined ? [] : [sql`event_type = ${request.type}`]), ])} LIMIT 1 `, }); const hasEventAfter: OrchestrationEventStoreShape["hasEventAfter"] = (input) => findEventAfter(input).pipe( Effect.map(Option.isSome), Effect.mapError( toPersistenceSqlOrDecodeError( "OrchestrationEventStore.hasEventAfter:query", "OrchestrationEventStore.hasEventAfter:decodeRow", ), ), ); const readAggregateRange: OrchestrationEventStoreShape["readAggregateRange"] = (input) => { const limit = Math.max(0, Math.floor(input.limit ?? DEFAULT_READ_FROM_SEQUENCE_LIMIT)); if (limit === 0 || input.fromSequenceExclusive >= input.toSequenceInclusive) { return Stream.empty; } return Stream.paginate( { cursor: input.fromSequenceExclusive, remaining: limit }, ({ cursor, remaining }) => readAggregateEventRows({ ...input, fromSequenceExclusive: cursor, limit: Math.min(remaining, READ_PAGE_SIZE), }).pipe( Effect.mapError( toPersistenceSqlOrDecodeError( "OrchestrationEventStore.readAggregateRange:query", "OrchestrationEventStore.readAggregateRange:decodeRows", ), ), Effect.flatMap((rows) => Effect.forEach(rows, (row) => decodeEvent(row).pipe( Effect.mapError( toPersistenceDecodeError("OrchestrationEventStore.readAggregateRange:rowToEvent"), ), ), ), ), Effect.map((events) => { const last = events.at(-1); const nextRemaining = remaining - events.length; return [ events, last === undefined || events.length < READ_PAGE_SIZE || nextRemaining === 0 || last.sequence >= input.toSequenceInclusive ? Option.none() : Option.some({ cursor: last.sequence, remaining: nextRemaining }), ] as const; }), ), ); }; const getAggregateReplayStats: OrchestrationEventStoreShape["getAggregateReplayStats"] = ( input, ) => readAggregateReplayStats({ ...input, limit: Math.max(0, Math.floor(input.maxEvents)) + 1, }).pipe( Effect.mapError( toPersistenceSqlOrDecodeError( "OrchestrationEventStore.getAggregateReplayStats:query", "OrchestrationEventStore.getAggregateReplayStats:decodeRow", ), ), Effect.map((row) => ({ ...row, hasCreateEvent: row.hasCreateEvent !== 0 })), ); return { append, readFromSequence, readAggregateRange, getAggregateReplayStats, readAll: () => readFromSequence(0, Number.MAX_SAFE_INTEGER), hasEventAfter, } satisfies OrchestrationEventStoreShape;}); export const OrchestrationEventStoreLive = Layer.effect(OrchestrationEventStore, makeEventStore);
import {
CommandId,
EventId,
IsoDateTime,
NonNegativeInt,
OrchestrationActorKind,
OrchestrationAggregateKind,
OrchestrationEvent,
OrchestrationEventMetadata,
OrchestrationEventType,
ProjectId,
ThreadId,
} from "@t3tools/contracts";
import * as SqlClient from "effect/unstable/sql/SqlClient";
import * as SqlSchema from "effect/unstable/sql/SqlSchema";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
import * as Option from "effect/Option";
import * as Schema from "effect/Schema";
import * as Stream from "effect/Stream";
import {
toPersistenceDecodeError,
toPersistenceSqlError,
type OrchestrationEventStoreError,
} from "../Errors.ts";
import {
OrchestrationEventStore,
type OrchestrationEventStoreShape,
} from "../Services/OrchestrationEventStore.ts";
const decodeEvent = Schema.decodeUnknownEffect(OrchestrationEvent);
const UnknownFromJsonString = Schema.fromJsonString(Schema.Unknown);
const EventMetadataFromJsonString = Schema.fromJsonString(OrchestrationEventMetadata);
const AppendEventRequestSchema = Schema.Struct({
eventId: EventId,
aggregateKind: OrchestrationAggregateKind,
streamId: Schema.Union([ProjectId, ThreadId]),
type: OrchestrationEventType,
causationEventId: Schema.NullOr(EventId),
correlationId: Schema.NullOr(CommandId),
actorKind: OrchestrationActorKind,
occurredAt: IsoDateTime,
commandId: Schema.NullOr(CommandId),
payloadJson: UnknownFromJsonString,
metadataJson: EventMetadataFromJsonString,
});
const OrchestrationEventPersistedRowSchema = Schema.Struct({
sequence: NonNegativeInt,
eventId: EventId,
type: OrchestrationEventType,
aggregateKind: OrchestrationAggregateKind,
aggregateId: Schema.Union([ProjectId, ThreadId]),
occurredAt: IsoDateTime,
commandId: Schema.NullOr(CommandId),
causationEventId: Schema.NullOr(EventId),
correlationId: Schema.NullOr(CommandId),
payload: UnknownFromJsonString,
metadata: EventMetadataFromJsonString,
});
const HasEventAfterRequestSchema = Schema.Struct({
aggregateKind: Schema.String,
aggregateId: Schema.String,
type: Schema.optional(Schema.String),
sequenceExclusive: NonNegativeInt,
});
const ReadFromSequenceRequestSchema = Schema.Struct({
sequenceExclusive: NonNegativeInt,
limit: Schema.Number,
});
const AggregateReplayRequestSchema = Schema.Struct({
aggregateKind: OrchestrationAggregateKind,
aggregateId: Schema.String,
fromSequenceExclusive: NonNegativeInt,
toSequenceInclusive: NonNegativeInt,
limit: Schema.Number,
});
const AggregateReplayStatsRowSchema = Schema.Struct({
eventCount: Schema.Number,
payloadBytes: Schema.Number,
hasCreateEvent: Schema.Number,
});
const DEFAULT_READ_FROM_SEQUENCE_LIMIT = 1_000;
const READ_PAGE_SIZE = 500;
function inferActorKind(
event: Omit<OrchestrationEvent, "sequence">,
): Schema.Schema.Type<typeof OrchestrationActorKind> {
if (event.commandId !== null && event.commandId.startsWith("provider:")) {
return "provider";
}
if (event.commandId !== null && event.commandId.startsWith("server:")) {
return "server";
}
if (
event.metadata.providerTurnId !== undefined ||
event.metadata.providerItemId !== undefined ||
event.metadata.adapterKey !== undefined
) {
return "provider";
}
if (event.commandId === null) {
return "server";
}
return "client";
}
function toPersistenceSqlOrDecodeError(sqlOperation: string, decodeOperation: string) {
return (cause: unknown): OrchestrationEventStoreError =>
Schema.isSchemaError(cause)
? toPersistenceDecodeError(decodeOperation)(cause)
: toPersistenceSqlError(sqlOperation)(cause);
}
const makeEventStore = Effect.gen(function* () {
const sql = yield* SqlClient.SqlClient;
const appendEventRow = SqlSchema.findOne({
Request: AppendEventRequestSchema,
Result: OrchestrationEventPersistedRowSchema,
execute: (request) =>
sql`
INSERT INTO orchestration_events (
event_id,
aggregate_kind,
stream_id,
stream_version,
event_type,
occurred_at,
command_id,
causation_event_id,
correlation_id,
actor_kind,
payload_json,
metadata_json
)
VALUES (
${request.eventId},
${request.aggregateKind},
${request.streamId},
COALESCE(
(
SELECT stream_version + 1
FROM orchestration_events
WHERE aggregate_kind = ${request.aggregateKind}
AND stream_id = ${request.streamId}
ORDER BY stream_version DESC
LIMIT 1
),
0
),
${request.type},
${request.occurredAt},
${request.commandId},
${request.causationEventId},
${request.correlationId},
${request.actorKind},
${request.payloadJson},
${request.metadataJson}
)
RETURNING
sequence,
event_id AS "eventId",
event_type AS "type",
aggregate_kind AS "aggregateKind",
stream_id AS "aggregateId",
occurred_at AS "occurredAt",
command_id AS "commandId",
causation_event_id AS "causationEventId",
correlation_id AS "correlationId",
payload_json AS "payload",
metadata_json AS "metadata"
`,
});
const readEventRowsFromSequence = SqlSchema.findAll({
Request: ReadFromSequenceRequestSchema,
Result: OrchestrationEventPersistedRowSchema,
execute: (request) =>
sql`
SELECT
sequence,
event_id AS "eventId",
event_type AS "type",
aggregate_kind AS "aggregateKind",
stream_id AS "aggregateId",
occurred_at AS "occurredAt",
command_id AS "commandId",
causation_event_id AS "causationEventId",
correlation_id AS "correlationId",
payload_json AS "payload",
metadata_json AS "metadata"
FROM orchestration_events
WHERE sequence > ${request.sequenceExclusive}
ORDER BY sequence ASC
LIMIT ${request.limit}
`,
});
const readAggregateEventRows = SqlSchema.findAll({
Request: AggregateReplayRequestSchema,
Result: OrchestrationEventPersistedRowSchema,
execute: (request) =>
sql`
SELECT
sequence,
event_id AS "eventId",
event_type AS "type",
aggregate_kind AS "aggregateKind",
stream_id AS "aggregateId",
occurred_at AS "occurredAt",
command_id AS "commandId",
causation_event_id AS "causationEventId",
correlation_id AS "correlationId",
payload_json AS "payload",
metadata_json AS "metadata"
FROM orchestration_events
WHERE aggregate_kind = ${request.aggregateKind}
AND stream_id = ${request.aggregateId}
AND sequence > ${request.fromSequenceExclusive}
AND sequence <= ${request.toSequenceInclusive}
ORDER BY sequence ASC
LIMIT ${request.limit}
`,
});
const readAggregateReplayStats = SqlSchema.findOne({
Request: AggregateReplayRequestSchema,
Result: AggregateReplayStatsRowSchema,
execute: (request) =>
sql`
SELECT
COUNT(*) AS "eventCount",
COALESCE(SUM(octet_length(payload_json)), 0) AS "payloadBytes",
COALESCE(MAX(event_type IN (
'thread.created', 'project.created'
)), 0) AS "hasCreateEvent"
FROM (
SELECT payload_json, event_type
FROM orchestration_events
WHERE aggregate_kind = ${request.aggregateKind}
AND stream_id = ${request.aggregateId}
AND sequence > ${request.fromSequenceExclusive}
AND sequence <= ${request.toSequenceInclusive}
ORDER BY sequence ASC
LIMIT ${request.limit}
)
`,
});
const append: OrchestrationEventStoreShape["append"] = (event) =>
appendEventRow({
eventId: event.eventId,
aggregateKind: event.aggregateKind,
streamId: event.aggregateId,
type: event.type,
causationEventId: event.causationEventId,
correlationId: event.correlationId,
actorKind: inferActorKind(event),
occurredAt: event.occurredAt,
commandId: event.commandId,
payloadJson: event.payload,
metadataJson: event.metadata,
}).pipe(
Effect.mapError(
toPersistenceSqlOrDecodeError(
"OrchestrationEventStore.append:insert",
"OrchestrationEventStore.append:decodeRow",
),
),
Effect.flatMap((row) =>
decodeEvent(row).pipe(
Effect.mapError(toPersistenceDecodeError("OrchestrationEventStore.append:rowToEvent")),
),
),
);
const readFromSequence: OrchestrationEventStoreShape["readFromSequence"] = (
sequenceExclusive,
limit = DEFAULT_READ_FROM_SEQUENCE_LIMIT,
) => {
const normalizedLimit = Math.max(0, Math.floor(limit));
if (normalizedLimit === 0) {
return Stream.empty;
}
return Stream.paginate(
{ cursor: sequenceExclusive, remaining: normalizedLimit },
({ cursor, remaining }) =>
readEventRowsFromSequence({
sequenceExclusive: cursor,
limit: Math.min(remaining, READ_PAGE_SIZE),
}).pipe(
Effect.mapError(
toPersistenceSqlOrDecodeError(
"OrchestrationEventStore.readFromSequence:query",
"OrchestrationEventStore.readFromSequence:decodeRows",
),
),
Effect.flatMap((rows) =>
Effect.forEach(rows, (row) =>
decodeEvent(row).pipe(
Effect.mapError(
toPersistenceDecodeError("OrchestrationEventStore.readFromSequence:rowToEvent"),
),
),
),
),
Effect.map((events) => {
const last = events.at(-1);
const nextRemaining = remaining - events.length;
return [
events,
last === undefined || nextRemaining <= 0
? Option.none()
: Option.some({ cursor: last.sequence, remaining: nextRemaining }),
] as const;
}),
),
);
};
const findEventAfter = SqlSchema.findOneOption({
Request: HasEventAfterRequestSchema,
Result: Schema.Struct({ sequence: Schema.Number }),
execute: (request) => sql`
SELECT sequence
FROM orchestration_events
WHERE aggregate_kind = ${request.aggregateKind}
AND stream_id = ${request.aggregateId}
AND ${sql.and([
sql`sequence > ${request.sequenceExclusive}`,
...(request.type === undefined ? [] : [sql`event_type = ${request.type}`]),
])}
LIMIT 1
`,
});
const hasEventAfter: OrchestrationEventStoreShape["hasEventAfter"] = (input) =>
findEventAfter(input).pipe(
Effect.map(Option.isSome),
Effect.mapError(
toPersistenceSqlOrDecodeError(
"OrchestrationEventStore.hasEventAfter:query",
"OrchestrationEventStore.hasEventAfter:decodeRow",
),
),
);
const readAggregateRange: OrchestrationEventStoreShape["readAggregateRange"] = (input) => {
const limit = Math.max(0, Math.floor(input.limit ?? DEFAULT_READ_FROM_SEQUENCE_LIMIT));
if (limit === 0 || input.fromSequenceExclusive >= input.toSequenceInclusive) {
return Stream.empty;
}
return Stream.paginate(
{ cursor: input.fromSequenceExclusive, remaining: limit },
({ cursor, remaining }) =>
readAggregateEventRows({
...input,
fromSequenceExclusive: cursor,
limit: Math.min(remaining, READ_PAGE_SIZE),
}).pipe(
Effect.mapError(
toPersistenceSqlOrDecodeError(
"OrchestrationEventStore.readAggregateRange:query",
"OrchestrationEventStore.readAggregateRange:decodeRows",
),
),
Effect.flatMap((rows) =>
Effect.forEach(rows, (row) =>
decodeEvent(row).pipe(
Effect.mapError(
toPersistenceDecodeError("OrchestrationEventStore.readAggregateRange:rowToEvent"),
),
),
),
),
Effect.map((events) => {
const last = events.at(-1);
const nextRemaining = remaining - events.length;
return [
events,
last === undefined ||
events.length < READ_PAGE_SIZE ||
nextRemaining === 0 ||
last.sequence >= input.toSequenceInclusive
? Option.none()
: Option.some({ cursor: last.sequence, remaining: nextRemaining }),
] as const;
}),
),
);
};
const getAggregateReplayStats: OrchestrationEventStoreShape["getAggregateReplayStats"] = (
input,
) =>
readAggregateReplayStats({
...input,
limit: Math.max(0, Math.floor(input.maxEvents)) + 1,
}).pipe(
Effect.mapError(
toPersistenceSqlOrDecodeError(
"OrchestrationEventStore.getAggregateReplayStats:query",
"OrchestrationEventStore.getAggregateReplayStats:decodeRow",
),
),
Effect.map((row) => ({ ...row, hasCreateEvent: row.hasCreateEvent !== 0 })),
);
return {
append,
readFromSequence,
readAggregateRange,
getAggregateReplayStats,
readAll: () => readFromSequence(0, Number.MAX_SAFE_INTEGER),
hasEventAfter,
} satisfies OrchestrationEventStoreShape;
});
export const OrchestrationEventStoreLive = Layer.effect(OrchestrationEventStore, makeEventStore);