effect-dynamodb 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +69 -0
  3. package/dist/Aggregate.d.ts +210 -0
  4. package/dist/Aggregate.d.ts.map +1 -0
  5. package/dist/Aggregate.js +1169 -0
  6. package/dist/Aggregate.js.map +1 -0
  7. package/dist/Batch.d.ts +62 -0
  8. package/dist/Batch.d.ts.map +1 -0
  9. package/dist/Batch.js +218 -0
  10. package/dist/Batch.js.map +1 -0
  11. package/dist/Collection.d.ts +58 -0
  12. package/dist/Collection.d.ts.map +1 -0
  13. package/dist/Collection.js +195 -0
  14. package/dist/Collection.js.map +1 -0
  15. package/dist/Collections.d.ts +121 -0
  16. package/dist/Collections.d.ts.map +1 -0
  17. package/dist/Collections.js +121 -0
  18. package/dist/Collections.js.map +1 -0
  19. package/dist/DynamoClient.d.ts +317 -0
  20. package/dist/DynamoClient.d.ts.map +1 -0
  21. package/dist/DynamoClient.js +653 -0
  22. package/dist/DynamoClient.js.map +1 -0
  23. package/dist/DynamoModel.d.ts +278 -0
  24. package/dist/DynamoModel.d.ts.map +1 -0
  25. package/dist/DynamoModel.js +453 -0
  26. package/dist/DynamoModel.js.map +1 -0
  27. package/dist/DynamoSchema.d.ts +137 -0
  28. package/dist/DynamoSchema.d.ts.map +1 -0
  29. package/dist/DynamoSchema.js +188 -0
  30. package/dist/DynamoSchema.js.map +1 -0
  31. package/dist/Entity.d.ts +628 -0
  32. package/dist/Entity.d.ts.map +1 -0
  33. package/dist/Entity.js +2371 -0
  34. package/dist/Entity.js.map +1 -0
  35. package/dist/Errors.d.ts +227 -0
  36. package/dist/Errors.d.ts.map +1 -0
  37. package/dist/Errors.js +93 -0
  38. package/dist/Errors.js.map +1 -0
  39. package/dist/EventStore.d.ts +208 -0
  40. package/dist/EventStore.d.ts.map +1 -0
  41. package/dist/EventStore.js +333 -0
  42. package/dist/EventStore.js.map +1 -0
  43. package/dist/Expression.d.ts +50 -0
  44. package/dist/Expression.d.ts.map +1 -0
  45. package/dist/Expression.js +170 -0
  46. package/dist/Expression.js.map +1 -0
  47. package/dist/KeyComposer.d.ts +125 -0
  48. package/dist/KeyComposer.d.ts.map +1 -0
  49. package/dist/KeyComposer.js +257 -0
  50. package/dist/KeyComposer.js.map +1 -0
  51. package/dist/Marshaller.d.ts +30 -0
  52. package/dist/Marshaller.d.ts.map +1 -0
  53. package/dist/Marshaller.js +115 -0
  54. package/dist/Marshaller.js.map +1 -0
  55. package/dist/Projection.d.ts +28 -0
  56. package/dist/Projection.d.ts.map +1 -0
  57. package/dist/Projection.js +39 -0
  58. package/dist/Projection.js.map +1 -0
  59. package/dist/Query.d.ts +214 -0
  60. package/dist/Query.d.ts.map +1 -0
  61. package/dist/Query.js +397 -0
  62. package/dist/Query.js.map +1 -0
  63. package/dist/Table.d.ts +178 -0
  64. package/dist/Table.d.ts.map +1 -0
  65. package/dist/Table.js +211 -0
  66. package/dist/Table.js.map +1 -0
  67. package/dist/Transaction.d.ts +79 -0
  68. package/dist/Transaction.d.ts.map +1 -0
  69. package/dist/Transaction.js +215 -0
  70. package/dist/Transaction.js.map +1 -0
  71. package/dist/_AggregateCursor.d.ts +44 -0
  72. package/dist/_AggregateCursor.d.ts.map +1 -0
  73. package/dist/_AggregateCursor.js +18 -0
  74. package/dist/_AggregateCursor.js.map +1 -0
  75. package/dist/_AggregateEdges.d.ts +77 -0
  76. package/dist/_AggregateEdges.d.ts.map +1 -0
  77. package/dist/_AggregateEdges.js +41 -0
  78. package/dist/_AggregateEdges.js.map +1 -0
  79. package/dist/_AggregateSchemas.d.ts +69 -0
  80. package/dist/_AggregateSchemas.d.ts.map +1 -0
  81. package/dist/_AggregateSchemas.js +194 -0
  82. package/dist/_AggregateSchemas.js.map +1 -0
  83. package/dist/_AggregateTypes.d.ts +94 -0
  84. package/dist/_AggregateTypes.d.ts.map +1 -0
  85. package/dist/_AggregateTypes.js +7 -0
  86. package/dist/_AggregateTypes.js.map +1 -0
  87. package/dist/_EntityCombinators.d.ts +155 -0
  88. package/dist/_EntityCombinators.d.ts.map +1 -0
  89. package/dist/_EntityCombinators.js +158 -0
  90. package/dist/_EntityCombinators.js.map +1 -0
  91. package/dist/_EntityConfig.d.ts +100 -0
  92. package/dist/_EntityConfig.d.ts.map +1 -0
  93. package/dist/_EntityConfig.js +7 -0
  94. package/dist/_EntityConfig.js.map +1 -0
  95. package/dist/_EntityOps.d.ts +205 -0
  96. package/dist/_EntityOps.d.ts.map +1 -0
  97. package/dist/_EntityOps.js +137 -0
  98. package/dist/_EntityOps.js.map +1 -0
  99. package/dist/_EntitySchemas.d.ts +57 -0
  100. package/dist/_EntitySchemas.d.ts.map +1 -0
  101. package/dist/_EntitySchemas.js +298 -0
  102. package/dist/_EntitySchemas.js.map +1 -0
  103. package/dist/_EntityTypes.d.ts +81 -0
  104. package/dist/_EntityTypes.d.ts.map +1 -0
  105. package/dist/_EntityTypes.js +7 -0
  106. package/dist/_EntityTypes.js.map +1 -0
  107. package/dist/_sparse_check.d.ts +2 -0
  108. package/dist/_sparse_check.d.ts.map +1 -0
  109. package/dist/_sparse_check.js +193 -0
  110. package/dist/_sparse_check.js.map +1 -0
  111. package/dist/index.d.ts +35 -0
  112. package/dist/index.d.ts.map +1 -0
  113. package/dist/index.js +20 -0
  114. package/dist/index.js.map +1 -0
  115. package/dist/internal/AggregateCursor.d.ts +44 -0
  116. package/dist/internal/AggregateCursor.d.ts.map +1 -0
  117. package/dist/internal/AggregateCursor.js +18 -0
  118. package/dist/internal/AggregateCursor.js.map +1 -0
  119. package/dist/internal/AggregateEdges.d.ts +77 -0
  120. package/dist/internal/AggregateEdges.d.ts.map +1 -0
  121. package/dist/internal/AggregateEdges.js +41 -0
  122. package/dist/internal/AggregateEdges.js.map +1 -0
  123. package/dist/internal/AggregateSchemas.d.ts +69 -0
  124. package/dist/internal/AggregateSchemas.d.ts.map +1 -0
  125. package/dist/internal/AggregateSchemas.js +193 -0
  126. package/dist/internal/AggregateSchemas.js.map +1 -0
  127. package/dist/internal/AggregateTypes.d.ts +94 -0
  128. package/dist/internal/AggregateTypes.d.ts.map +1 -0
  129. package/dist/internal/AggregateTypes.js +7 -0
  130. package/dist/internal/AggregateTypes.js.map +1 -0
  131. package/dist/internal/BoundQuery.d.ts +107 -0
  132. package/dist/internal/BoundQuery.d.ts.map +1 -0
  133. package/dist/internal/BoundQuery.js +111 -0
  134. package/dist/internal/BoundQuery.js.map +1 -0
  135. package/dist/internal/EntityCombinators.d.ts +173 -0
  136. package/dist/internal/EntityCombinators.d.ts.map +1 -0
  137. package/dist/internal/EntityCombinators.js +183 -0
  138. package/dist/internal/EntityCombinators.js.map +1 -0
  139. package/dist/internal/EntityConfig.d.ts +100 -0
  140. package/dist/internal/EntityConfig.d.ts.map +1 -0
  141. package/dist/internal/EntityConfig.js +7 -0
  142. package/dist/internal/EntityConfig.js.map +1 -0
  143. package/dist/internal/EntityOps.d.ts +268 -0
  144. package/dist/internal/EntityOps.d.ts.map +1 -0
  145. package/dist/internal/EntityOps.js +153 -0
  146. package/dist/internal/EntityOps.js.map +1 -0
  147. package/dist/internal/EntitySchemas.d.ts +57 -0
  148. package/dist/internal/EntitySchemas.d.ts.map +1 -0
  149. package/dist/internal/EntitySchemas.js +299 -0
  150. package/dist/internal/EntitySchemas.js.map +1 -0
  151. package/dist/internal/EntityTypes.d.ts +128 -0
  152. package/dist/internal/EntityTypes.d.ts.map +1 -0
  153. package/dist/internal/EntityTypes.js +7 -0
  154. package/dist/internal/EntityTypes.js.map +1 -0
  155. package/dist/internal/Expr.d.ts +141 -0
  156. package/dist/internal/Expr.d.ts.map +1 -0
  157. package/dist/internal/Expr.js +332 -0
  158. package/dist/internal/Expr.js.map +1 -0
  159. package/dist/internal/PathBuilder.d.ts +88 -0
  160. package/dist/internal/PathBuilder.d.ts.map +1 -0
  161. package/dist/internal/PathBuilder.js +100 -0
  162. package/dist/internal/PathBuilder.js.map +1 -0
  163. package/dist/internal/TransactableOps.d.ts +25 -0
  164. package/dist/internal/TransactableOps.d.ts.map +1 -0
  165. package/dist/internal/TransactableOps.js +58 -0
  166. package/dist/internal/TransactableOps.js.map +1 -0
  167. package/dist/internal/sparse_check.d.ts +2 -0
  168. package/dist/internal/sparse_check.d.ts.map +1 -0
  169. package/dist/internal/sparse_check.js +196 -0
  170. package/dist/internal/sparse_check.js.map +1 -0
  171. package/package.json +71 -0
@@ -0,0 +1,208 @@
1
+ /**
2
+ * EventStore — Typed, Effect-native event sourcing on DynamoDB.
3
+ *
4
+ * Provides:
5
+ * - `Decider` type for command-event-state modeling
6
+ * - `makeStream` factory for creating event streams bound to a Table
7
+ * - Core operations: `append`, `read`, `readFrom`, `currentVersion`
8
+ * - `commandHandler` combinator for read-decide-append cycle
9
+ * - `fold` / `foldFrom` helpers for state reconstruction
10
+ *
11
+ * Built on the existing library primitives (DynamoSchema, KeyComposer, Query,
12
+ * DynamoClient, Marshaller).
13
+ */
14
+ import { Effect, Schema } from "effect";
15
+ import { DynamoClient, type DynamoClientError } from "./DynamoClient.js";
16
+ import { TransactionCancelled, ValidationError, VersionConflict } from "./Errors.js";
17
+ import * as Query from "./Query.js";
18
+ import type { Table, TableConfig } from "./Table.js";
19
+ /**
20
+ * A Decider encodes the command-event-state triad for an aggregate.
21
+ *
22
+ * - `decide` — given a command and current state, produce events (or fail with E)
23
+ * - `evolve` — pure left fold: apply one event to a state
24
+ * - `initialState` — starting state for a new aggregate
25
+ */
26
+ export interface Decider<State, Command, Event, E = never> {
27
+ readonly decide: (command: Command, state: State) => Effect.Effect<ReadonlyArray<Event>, E>;
28
+ readonly evolve: (state: State, event: Event) => State;
29
+ readonly initialState: State;
30
+ }
31
+ /**
32
+ * A persisted event read from a stream, enriched with stream metadata.
33
+ */
34
+ export interface StreamEvent<A> {
35
+ readonly streamId: string;
36
+ readonly version: number;
37
+ readonly eventType: string;
38
+ readonly data: A;
39
+ readonly metadata: Record<string, unknown> | undefined;
40
+ readonly timestamp: string;
41
+ }
42
+ /** Result of appending events to a stream. */
43
+ export interface AppendResult<A> {
44
+ readonly version: number;
45
+ readonly events: ReadonlyArray<A>;
46
+ }
47
+ /** Result of a command handler execution. */
48
+ export interface CommandHandlerResult<State, Event> extends AppendResult<Event> {
49
+ readonly state: State;
50
+ }
51
+ type StreamIdInput<T extends ReadonlyArray<string>> = {
52
+ readonly [K in T[number]]: string;
53
+ };
54
+ declare const EventStreamTypeId: unique symbol;
55
+ export type EventStreamTypeId = typeof EventStreamTypeId;
56
+ /**
57
+ * An EventStream is the repository for a named event stream.
58
+ *
59
+ * Created via {@link makeStream}. Operations are called directly on the stream:
60
+ * `MatchEvents.append(...)`, `MatchEvents.read(...)`, `MatchEvents.query.events(...)`.
61
+ */
62
+ export interface EventStream<TEvent, TStreamIdFields extends ReadonlyArray<string>, TMetadata> {
63
+ readonly [EventStreamTypeId]: EventStreamTypeId;
64
+ readonly streamName: string;
65
+ readonly eventSchema: Schema.Top;
66
+ readonly append: (streamId: StreamIdInput<TStreamIdFields>, events: ReadonlyArray<TEvent>, expectedVersion: number, options?: {
67
+ readonly metadata?: TMetadata;
68
+ } | undefined) => Effect.Effect<AppendResult<TEvent>, VersionConflict | DynamoClientError | ValidationError | TransactionCancelled, DynamoClient | TableConfig>;
69
+ readonly read: (streamId: StreamIdInput<TStreamIdFields>) => Effect.Effect<ReadonlyArray<StreamEvent<TEvent>>, DynamoClientError | ValidationError, DynamoClient | TableConfig>;
70
+ readonly readFrom: (streamId: StreamIdInput<TStreamIdFields>, afterVersion: number) => Effect.Effect<ReadonlyArray<StreamEvent<TEvent>>, DynamoClientError | ValidationError, DynamoClient | TableConfig>;
71
+ readonly currentVersion: (streamId: StreamIdInput<TStreamIdFields>) => Effect.Effect<number, DynamoClientError | ValidationError, DynamoClient | TableConfig>;
72
+ readonly query: {
73
+ readonly events: (streamId: StreamIdInput<TStreamIdFields>) => Query.Query<StreamEvent<TEvent>>;
74
+ };
75
+ }
76
+ /**
77
+ * Create an EventStream bound to a Table.
78
+ *
79
+ * @example
80
+ * ```typescript
81
+ * class MatchStarted extends Schema.Class<MatchStarted>("MatchStarted")({
82
+ * venue: Schema.String,
83
+ * }) {}
84
+ *
85
+ * class InningsCompleted extends Schema.Class<InningsCompleted>("InningsCompleted")({
86
+ * innings: Schema.Number,
87
+ * runs: Schema.Number,
88
+ * }) {}
89
+ *
90
+ * const MatchEvents = EventStore.makeStream({
91
+ * table: EventsTable,
92
+ * streamName: "Match",
93
+ * events: [MatchStarted, InningsCompleted],
94
+ * streamId: { composite: ["matchId"] },
95
+ * })
96
+ * ```
97
+ */
98
+ export declare const makeStream: <const TEvents extends ReadonlyArray<Schema.Top>, TTable extends Table, const TStreamName extends string, const TStreamId extends {
99
+ readonly composite: ReadonlyArray<string>;
100
+ }, TMetadata extends Schema.Top | undefined = undefined>(config: {
101
+ readonly table: TTable;
102
+ readonly streamName: TStreamName;
103
+ readonly events: TEvents;
104
+ readonly streamId: TStreamId;
105
+ readonly metadata?: TMetadata;
106
+ }) => EventStream<Schema.Schema.Type<TEvents[number]>, TStreamId["composite"], TMetadata extends Schema.Top ? Schema.Schema.Type<TMetadata> : undefined>;
107
+ /**
108
+ * An EventStream whose operations have `DynamoClient` and `TableConfig` already
109
+ * resolved, so all methods return `Effect<A, E, never>`.
110
+ *
111
+ * Created via {@link bind}. Use in service layers to avoid leaking infrastructure
112
+ * requirements through service method signatures.
113
+ *
114
+ * @example
115
+ * ```typescript
116
+ * export class MatchEventService extends Context.Service<MatchEventService>()("MatchEventService", {
117
+ * make: Effect.gen(function* () {
118
+ * const stream = yield* EventStore.bind(MatchEvents)
119
+ * return {
120
+ * append: (matchId, events, version) => stream.append({ matchId }, events, version),
121
+ * read: (matchId) => stream.read({ matchId }),
122
+ * }
123
+ * }),
124
+ * }) {}
125
+ * ```
126
+ */
127
+ export interface BoundEventStream<TEvent, TStreamIdFields extends ReadonlyArray<string>, TMetadata> {
128
+ readonly [EventStreamTypeId]: EventStreamTypeId;
129
+ readonly streamName: string;
130
+ readonly eventSchema: Schema.Top;
131
+ readonly append: (streamId: StreamIdInput<TStreamIdFields>, events: ReadonlyArray<TEvent>, expectedVersion: number, options?: {
132
+ readonly metadata?: TMetadata;
133
+ } | undefined) => Effect.Effect<AppendResult<TEvent>, VersionConflict | DynamoClientError | ValidationError | TransactionCancelled, never>;
134
+ readonly read: (streamId: StreamIdInput<TStreamIdFields>) => Effect.Effect<ReadonlyArray<StreamEvent<TEvent>>, DynamoClientError | ValidationError, never>;
135
+ readonly readFrom: (streamId: StreamIdInput<TStreamIdFields>, afterVersion: number) => Effect.Effect<ReadonlyArray<StreamEvent<TEvent>>, DynamoClientError | ValidationError, never>;
136
+ readonly currentVersion: (streamId: StreamIdInput<TStreamIdFields>) => Effect.Effect<number, DynamoClientError | ValidationError, never>;
137
+ readonly query: {
138
+ readonly events: (streamId: StreamIdInput<TStreamIdFields>) => Query.Query<StreamEvent<TEvent>>;
139
+ };
140
+ /** Escape hatch: provide DynamoClient | TableConfig to an arbitrary effect. */
141
+ readonly provide: <A, E>(effect: Effect.Effect<A, E, DynamoClient | TableConfig>) => Effect.Effect<A, E, never>;
142
+ }
143
+ /**
144
+ * Bind an EventStream to resolved `DynamoClient` and `TableConfig` services.
145
+ * Returns a {@link BoundEventStream} where all operations have `R = never`.
146
+ *
147
+ * Use inside `Context.Service` make effects to prevent service methods
148
+ * from leaking infrastructure requirements.
149
+ *
150
+ * @example
151
+ * ```typescript
152
+ * const stream = yield* EventStore.bind(MatchEvents)
153
+ * const events = yield* stream.read({ matchId: "m-1" }) // R = never
154
+ * yield* stream.append({ matchId: "m-1" }, [event], 0) // R = never
155
+ * ```
156
+ */
157
+ export declare const bind: <TEvent, TStreamIdFields extends ReadonlyArray<string>, TMetadata>(stream: EventStream<TEvent, TStreamIdFields, TMetadata>) => Effect.Effect<BoundEventStream<TEvent, TStreamIdFields, TMetadata>, never, DynamoClient | TableConfig>;
158
+ type CommandHandler<State, Command, TEvent, E, TStreamIdFields extends ReadonlyArray<string>, TMetadata> = (streamId: StreamIdInput<TStreamIdFields>, command: Command, options?: {
159
+ readonly metadata?: TMetadata;
160
+ } | undefined) => Effect.Effect<CommandHandlerResult<State, TEvent>, E | VersionConflict | DynamoClientError | ValidationError | TransactionCancelled, DynamoClient | TableConfig>;
161
+ type BoundCommandHandler<State, Command, TEvent, E, TStreamIdFields extends ReadonlyArray<string>, TMetadata> = (streamId: StreamIdInput<TStreamIdFields>, command: Command, options?: {
162
+ readonly metadata?: TMetadata;
163
+ } | undefined) => Effect.Effect<CommandHandlerResult<State, TEvent>, E | VersionConflict | DynamoClientError | ValidationError | TransactionCancelled, never>;
164
+ /**
165
+ * Create a command handler that reads, decides, and appends atomically.
166
+ *
167
+ * Supports both data-first and data-last (pipeable) usage, and works with
168
+ * both `EventStream` (R = DynamoClient | TableConfig) and `BoundEventStream` (R = never).
169
+ *
170
+ * ```typescript
171
+ * // Data-first with EventStream
172
+ * const handle = EventStore.commandHandler(decider, stream)
173
+ *
174
+ * // Data-first with BoundEventStream
175
+ * const handle = EventStore.commandHandler(decider, boundStream)
176
+ *
177
+ * // Data-last (pipe)
178
+ * const handle = stream.pipe(EventStore.commandHandler(decider))
179
+ * ```
180
+ */
181
+ export declare const commandHandler: {
182
+ <State, Command, TEvent, E>(decider: Decider<State, Command, TEvent, E>): {
183
+ <TStreamIdFields extends ReadonlyArray<string>, TMetadata>(stream: BoundEventStream<TEvent, TStreamIdFields, TMetadata>): BoundCommandHandler<State, Command, TEvent, E, TStreamIdFields, TMetadata>;
184
+ <TStreamIdFields extends ReadonlyArray<string>, TMetadata>(stream: EventStream<TEvent, TStreamIdFields, TMetadata>): CommandHandler<State, Command, TEvent, E, TStreamIdFields, TMetadata>;
185
+ };
186
+ <State, Command, TEvent, E, TStreamIdFields extends ReadonlyArray<string>, TMetadata>(decider: Decider<State, Command, TEvent, E>, stream: BoundEventStream<TEvent, TStreamIdFields, TMetadata>): BoundCommandHandler<State, Command, TEvent, E, TStreamIdFields, TMetadata>;
187
+ <State, Command, TEvent, E, TStreamIdFields extends ReadonlyArray<string>, TMetadata>(decider: Decider<State, Command, TEvent, E>, stream: EventStream<TEvent, TStreamIdFields, TMetadata>): CommandHandler<State, Command, TEvent, E, TStreamIdFields, TMetadata>;
188
+ };
189
+ /**
190
+ * Reconstruct state from events by folding through a decider's `evolve` function.
191
+ *
192
+ * Pure synchronous — no DynamoDB access.
193
+ */
194
+ export declare const fold: {
195
+ <A>(events: ReadonlyArray<StreamEvent<A>>): <S, C, E>(decider: Decider<S, C, A, E>) => S;
196
+ <S, C, A, E>(decider: Decider<S, C, A, E>, events: ReadonlyArray<StreamEvent<A>>): S;
197
+ };
198
+ /**
199
+ * Fold from a starting state (e.g., snapshot + delta events).
200
+ *
201
+ * Pure synchronous — no DynamoDB access.
202
+ */
203
+ export declare const foldFrom: {
204
+ <A>(startState: unknown, events: ReadonlyArray<StreamEvent<A>>): <S, C, E>(decider: Decider<S, C, A, E>) => S;
205
+ <S, C, A, E>(decider: Decider<S, C, A, E>, startState: S, events: ReadonlyArray<StreamEvent<A>>): S;
206
+ };
207
+ export {};
208
+ //# sourceMappingURL=EventStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EventStore.d.ts","sourceRoot":"","sources":["../src/EventStore.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,333 @@
1
+ /**
2
+ * EventStore — Typed, Effect-native event sourcing on DynamoDB.
3
+ *
4
+ * Provides:
5
+ * - `Decider` type for command-event-state modeling
6
+ * - `makeStream` factory for creating event streams bound to a Table
7
+ * - Core operations: `append`, `read`, `readFrom`, `currentVersion`
8
+ * - `commandHandler` combinator for read-decide-append cycle
9
+ * - `fold` / `foldFrom` helpers for state reconstruction
10
+ *
11
+ * Built on the existing library primitives (DynamoSchema, KeyComposer, Query,
12
+ * DynamoClient, Marshaller).
13
+ */
14
+ import { Effect, Function, Schema } from "effect";
15
+ import { DynamoClient } from "./DynamoClient.js";
16
+ import * as DynamoSchema from "./DynamoSchema.js";
17
+ import { isAwsTransactionCancelled, TransactionCancelled, ValidationError, VersionConflict, } from "./Errors.js";
18
+ import * as KeyComposer from "./KeyComposer.js";
19
+ import { toAttributeMap } from "./Marshaller.js";
20
+ import * as Query from "./Query.js";
21
+ // ---------------------------------------------------------------------------
22
+ // EventStreamTypeId
23
+ // ---------------------------------------------------------------------------
24
+ const EventStreamTypeId = Symbol.for("effect-dynamodb/EventStream");
25
+ // ---------------------------------------------------------------------------
26
+ // makeStream factory
27
+ // ---------------------------------------------------------------------------
28
+ /**
29
+ * Create an EventStream bound to a Table.
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * class MatchStarted extends Schema.Class<MatchStarted>("MatchStarted")({
34
+ * venue: Schema.String,
35
+ * }) {}
36
+ *
37
+ * class InningsCompleted extends Schema.Class<InningsCompleted>("InningsCompleted")({
38
+ * innings: Schema.Number,
39
+ * runs: Schema.Number,
40
+ * }) {}
41
+ *
42
+ * const MatchEvents = EventStore.makeStream({
43
+ * table: EventsTable,
44
+ * streamName: "Match",
45
+ * events: [MatchStarted, InningsCompleted],
46
+ * streamId: { composite: ["matchId"] },
47
+ * })
48
+ * ```
49
+ */
50
+ export const makeStream = (config) => {
51
+ const schema = config.table.schema;
52
+ const entityType = `${config.streamName.toLowerCase()}.event`;
53
+ const compositeFields = config.streamId.composite;
54
+ // Build union schema from event schemas for decoding
55
+ const eventUnion = config.events.length === 1
56
+ ? config.events[0]
57
+ : Schema.Union(config.events);
58
+ // Metadata schema (optional)
59
+ const metadataSchema = config.metadata;
60
+ // ---------------------------------------------------------------------------
61
+ // Key helpers
62
+ // ---------------------------------------------------------------------------
63
+ const composeStreamPk = (streamId) => {
64
+ const composites = KeyComposer.extractComposites(compositeFields, streamId);
65
+ return DynamoSchema.composeKey(schema, config.streamName.toLowerCase(), composites);
66
+ };
67
+ const composeEventSk = (version) => DynamoSchema.composeEventVersionKey(schema, entityType, version);
68
+ // ---------------------------------------------------------------------------
69
+ // Decode a raw DynamoDB item → StreamEvent<TEvent>
70
+ // ---------------------------------------------------------------------------
71
+ const decodeStreamEvent = (raw) => Effect.gen(function* () {
72
+ const decoder = Schema.decodeUnknownEffect(eventUnion);
73
+ const data = yield* decoder(raw.data).pipe(Effect.mapError((cause) => new ValidationError({
74
+ entityType,
75
+ operation: "EventStore.decode",
76
+ cause,
77
+ })));
78
+ return {
79
+ streamId: raw.streamId,
80
+ version: raw.version,
81
+ eventType: raw.eventType,
82
+ data,
83
+ metadata: raw.metadata,
84
+ timestamp: raw.timestamp,
85
+ };
86
+ });
87
+ // ---------------------------------------------------------------------------
88
+ // append
89
+ // ---------------------------------------------------------------------------
90
+ const append = (streamId, events, expectedVersion, options) => Effect.gen(function* () {
91
+ if (events.length === 0) {
92
+ return { version: expectedVersion, events: [] };
93
+ }
94
+ const client = yield* DynamoClient;
95
+ const { name: tableName } = yield* config.table.Tag;
96
+ const pk = composeStreamPk(streamId);
97
+ const now = new Date().toISOString();
98
+ // Resolve stream ID string for storage (join composites)
99
+ const streamIdStr = compositeFields
100
+ .map((f) => streamId[f])
101
+ .join("#");
102
+ // Validate and encode metadata if schema provided
103
+ let encodedMetadata;
104
+ if (options?.metadata !== undefined && metadataSchema) {
105
+ const validated = yield* Schema.decodeUnknownEffect(metadataSchema)(options.metadata).pipe(Effect.mapError((cause) => new ValidationError({
106
+ entityType,
107
+ operation: "EventStore.append.metadata",
108
+ cause,
109
+ })));
110
+ encodedMetadata = validated;
111
+ }
112
+ else if (options?.metadata !== undefined) {
113
+ encodedMetadata = options.metadata;
114
+ }
115
+ // Build transact items — one Put per event, each with attribute_not_exists(pk)
116
+ const transactItems = events.map((event, i) => {
117
+ const version = expectedVersion + i + 1;
118
+ // In Effect v4, Schema.Class instances don't have _tag as an own property.
119
+ // The identifier is on the constructor (class) itself.
120
+ const evtType = event._tag ??
121
+ event.constructor.identifier ??
122
+ event.constructor.name;
123
+ const eventData = { _tag: evtType, ...event };
124
+ const item = {
125
+ pk,
126
+ sk: composeEventSk(version),
127
+ __edd_e__: entityType,
128
+ streamId: streamIdStr,
129
+ version,
130
+ eventType: evtType,
131
+ data: eventData,
132
+ timestamp: now,
133
+ };
134
+ if (encodedMetadata !== undefined) {
135
+ item.metadata = encodedMetadata;
136
+ }
137
+ return {
138
+ Put: {
139
+ TableName: tableName,
140
+ Item: toAttributeMap(item),
141
+ ConditionExpression: "attribute_not_exists(pk)",
142
+ },
143
+ };
144
+ });
145
+ yield* client.transactWriteItems({ TransactItems: transactItems }).pipe(Effect.mapError((error) => {
146
+ if (isAwsTransactionCancelled(error.cause)) {
147
+ const reasons = (error.cause.CancellationReasons ?? []).map((r) => ({
148
+ code: r?.Code,
149
+ message: r?.Message,
150
+ }));
151
+ const hasConflict = reasons.some((r) => r.code === "ConditionalCheckFailed");
152
+ if (hasConflict) {
153
+ return new VersionConflict({
154
+ streamName: config.streamName,
155
+ streamId: streamIdStr,
156
+ expectedVersion,
157
+ });
158
+ }
159
+ return new TransactionCancelled({
160
+ operation: "TransactWriteItems",
161
+ reasons,
162
+ cause: error.cause,
163
+ });
164
+ }
165
+ return error;
166
+ }));
167
+ return {
168
+ version: expectedVersion + events.length,
169
+ events,
170
+ };
171
+ });
172
+ // ---------------------------------------------------------------------------
173
+ // read
174
+ // ---------------------------------------------------------------------------
175
+ const read = (streamId) => Effect.gen(function* () {
176
+ const query = buildEventsQuery(streamId);
177
+ return yield* Query.collect(query);
178
+ });
179
+ // ---------------------------------------------------------------------------
180
+ // readFrom
181
+ // ---------------------------------------------------------------------------
182
+ const readFrom = (streamId, afterVersion) => Effect.gen(function* () {
183
+ const query = buildEventsQuery(streamId).pipe(Query.where({ gt: composeEventSk(afterVersion) }));
184
+ return yield* Query.collect(query);
185
+ });
186
+ // ---------------------------------------------------------------------------
187
+ // currentVersion
188
+ // ---------------------------------------------------------------------------
189
+ const currentVersion = (streamId) => Effect.gen(function* () {
190
+ const query = buildEventsQuery(streamId).pipe(Query.reverse, Query.limit(1));
191
+ const results = yield* Query.collect(query);
192
+ if (results.length === 0)
193
+ return 0;
194
+ return results[0].version;
195
+ });
196
+ // ---------------------------------------------------------------------------
197
+ // query.events helper
198
+ // ---------------------------------------------------------------------------
199
+ const buildEventsQuery = (streamId) => {
200
+ const pk = composeStreamPk(streamId);
201
+ return Query.make({
202
+ tableName: "",
203
+ indexName: undefined,
204
+ pkField: "pk",
205
+ pkValue: pk,
206
+ skField: "sk",
207
+ entityTypes: [entityType],
208
+ decoder: (raw) => decodeStreamEvent(raw),
209
+ resolveTableName: config.table.Tag.useSync((tc) => tc.name),
210
+ });
211
+ };
212
+ const queryNamespace = {
213
+ events: (streamId) => buildEventsQuery(streamId),
214
+ };
215
+ // ---------------------------------------------------------------------------
216
+ // Return EventStream
217
+ // ---------------------------------------------------------------------------
218
+ // Cast rationale: makeStream builds the stream object from closures that capture
219
+ // the generic config. The Table.Tag service has a dynamically-created tag whose R
220
+ // type parameter is opaque, causing Effect.gen to infer `unknown` for R. The cast
221
+ // is safe because all operations correctly require DynamoClient | TableConfig at
222
+ // runtime — the user must provide these layers.
223
+ return {
224
+ [EventStreamTypeId]: EventStreamTypeId,
225
+ streamName: config.streamName,
226
+ eventSchema: eventUnion,
227
+ append,
228
+ read,
229
+ readFrom,
230
+ currentVersion,
231
+ query: queryNamespace,
232
+ };
233
+ };
234
+ // ---------------------------------------------------------------------------
235
+ // EventStore.bind — resolve services, return BoundEventStream with R = never
236
+ // ---------------------------------------------------------------------------
237
+ /**
238
+ * Bind an EventStream to resolved `DynamoClient` and `TableConfig` services.
239
+ * Returns a {@link BoundEventStream} where all operations have `R = never`.
240
+ *
241
+ * Use inside `Context.Service` make effects to prevent service methods
242
+ * from leaking infrastructure requirements.
243
+ *
244
+ * @example
245
+ * ```typescript
246
+ * const stream = yield* EventStore.bind(MatchEvents)
247
+ * const events = yield* stream.read({ matchId: "m-1" }) // R = never
248
+ * yield* stream.append({ matchId: "m-1" }, [event], 0) // R = never
249
+ * ```
250
+ */
251
+ export const bind = (stream) => Effect.gen(function* () {
252
+ const ctx = yield* Effect.context();
253
+ const provide = (effect) => Effect.provide(effect, ctx);
254
+ return {
255
+ [EventStreamTypeId]: EventStreamTypeId,
256
+ streamName: stream.streamName,
257
+ eventSchema: stream.eventSchema,
258
+ append: (streamId, events, expectedVersion, options) => provide(stream.append(streamId, events, expectedVersion, options)),
259
+ read: (streamId) => provide(stream.read(streamId)),
260
+ readFrom: (streamId, afterVersion) => provide(stream.readFrom(streamId, afterVersion)),
261
+ currentVersion: (streamId) => provide(stream.currentVersion(streamId)),
262
+ query: stream.query,
263
+ provide,
264
+ };
265
+ });
266
+ /**
267
+ * Create a command handler that reads, decides, and appends atomically.
268
+ *
269
+ * Supports both data-first and data-last (pipeable) usage, and works with
270
+ * both `EventStream` (R = DynamoClient | TableConfig) and `BoundEventStream` (R = never).
271
+ *
272
+ * ```typescript
273
+ * // Data-first with EventStream
274
+ * const handle = EventStore.commandHandler(decider, stream)
275
+ *
276
+ * // Data-first with BoundEventStream
277
+ * const handle = EventStore.commandHandler(decider, boundStream)
278
+ *
279
+ * // Data-last (pipe)
280
+ * const handle = stream.pipe(EventStore.commandHandler(decider))
281
+ * ```
282
+ */
283
+ export const commandHandler = Function.dual(2, (decider, stream) => (streamId, command, options) => Effect.gen(function* () {
284
+ // 1. Read all events
285
+ const events = yield* stream.read(streamId);
286
+ // 2. Fold to current state
287
+ const curVersion = events.length > 0 ? events[events.length - 1].version : 0;
288
+ let state = decider.initialState;
289
+ for (const event of events) {
290
+ state = decider.evolve(state, event.data);
291
+ }
292
+ // 3. Decide
293
+ const newEvents = yield* decider.decide(command, state);
294
+ // 4. No-op command — return current state
295
+ if (newEvents.length === 0) {
296
+ return { state, version: curVersion, events: [] };
297
+ }
298
+ // 5. Append with optimistic concurrency
299
+ const result = yield* stream.append(streamId, newEvents, curVersion, options);
300
+ // 6. Evolve state through new events
301
+ for (const event of newEvents) {
302
+ state = decider.evolve(state, event);
303
+ }
304
+ return { state, version: result.version, events: newEvents };
305
+ }));
306
+ // ---------------------------------------------------------------------------
307
+ // fold helpers
308
+ // ---------------------------------------------------------------------------
309
+ /**
310
+ * Reconstruct state from events by folding through a decider's `evolve` function.
311
+ *
312
+ * Pure synchronous — no DynamoDB access.
313
+ */
314
+ export const fold = Function.dual(2, (decider, events) => {
315
+ let state = decider.initialState;
316
+ for (const event of events) {
317
+ state = decider.evolve(state, event.data);
318
+ }
319
+ return state;
320
+ });
321
+ /**
322
+ * Fold from a starting state (e.g., snapshot + delta events).
323
+ *
324
+ * Pure synchronous — no DynamoDB access.
325
+ */
326
+ export const foldFrom = Function.dual(3, (decider, startState, events) => {
327
+ let state = startState;
328
+ for (const event of events) {
329
+ state = decider.evolve(state, event.data);
330
+ }
331
+ return state;
332
+ });
333
+ //# sourceMappingURL=EventStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EventStore.js","sourceRoot":"","sources":["../src/EventStore.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Expression — Type-safe builders for DynamoDB expressions.
3
+ *
4
+ * Produces expression strings with attribute name/value placeholders,
5
+ * matching the shape used by DynamoDB query/put/update/delete operations.
6
+ *
7
+ * Three builder types:
8
+ * - condition: For conditional puts/deletes (ConditionExpression)
9
+ * - filter: For query result filtering (FilterExpression)
10
+ * - update: For update operations (UpdateExpression)
11
+ */
12
+ import type { AttributeValue } from "@aws-sdk/client-dynamodb";
13
+ export interface ExpressionResult {
14
+ readonly expression: string;
15
+ readonly names: Record<string, string>;
16
+ readonly values: Record<string, AttributeValue>;
17
+ }
18
+ export interface ConditionInput {
19
+ readonly eq?: Record<string, unknown> | undefined;
20
+ readonly ne?: Record<string, unknown> | undefined;
21
+ readonly lt?: Record<string, unknown> | undefined;
22
+ readonly le?: Record<string, unknown> | undefined;
23
+ readonly gt?: Record<string, unknown> | undefined;
24
+ readonly ge?: Record<string, unknown> | undefined;
25
+ readonly between?: Record<string, readonly [unknown, unknown]> | undefined;
26
+ readonly beginsWith?: Record<string, string> | undefined;
27
+ readonly attributeExists?: string | ReadonlyArray<string> | undefined;
28
+ readonly attributeNotExists?: string | ReadonlyArray<string> | undefined;
29
+ }
30
+ /**
31
+ * Build a ConditionExpression for conditional puts/deletes.
32
+ * All clauses are ANDed together.
33
+ */
34
+ export declare const condition: (input: ConditionInput) => ExpressionResult;
35
+ /**
36
+ * Build a FilterExpression for query result filtering.
37
+ * Same syntax as condition expressions.
38
+ */
39
+ export declare const filter: (input: ConditionInput) => ExpressionResult;
40
+ export interface UpdateInput {
41
+ readonly set?: Record<string, unknown> | undefined;
42
+ readonly remove?: ReadonlyArray<string> | undefined;
43
+ readonly add?: Record<string, number> | undefined;
44
+ readonly delete?: Record<string, unknown> | undefined;
45
+ }
46
+ /**
47
+ * Build an UpdateExpression with SET, REMOVE, ADD, DELETE clauses.
48
+ */
49
+ export declare const update: (input: UpdateInput) => ExpressionResult;
50
+ //# sourceMappingURL=Expression.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Expression.d.ts","sourceRoot":"","sources":["../src/Expression.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAK9D,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAA;CAChD;AAID,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAA;IACjD,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAA;IACjD,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAA;IACjD,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAA;IACjD,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAA;IACjD,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAA;IACjD,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,CAAA;IAC1E,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAA;IACxD,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,GAAG,SAAS,CAAA;IACrE,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,GAAG,SAAS,CAAA;CACzE;AAsGD;;;GAGG;AACH,eAAO,MAAM,SAAS,GAAI,OAAO,cAAc,KAAG,gBAAyC,CAAA;AAE3F;;;GAGG;AACH,eAAO,MAAM,MAAM,GAAI,OAAO,cAAc,KAAG,gBAAyC,CAAA;AAIxF,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAA;IAClD,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,SAAS,CAAA;IACnD,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAA;IACjD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAA;CACtD;AAED;;GAEG;AACH,eAAO,MAAM,MAAM,GAAI,OAAO,WAAW,KAAG,gBA8D3C,CAAA"}