@reventlessdev/reventless-core 3.0.0-alpha.136 → 3.0.0-alpha.138

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 (41) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/package.json +7 -7
  3. package/src/Message.res +43 -10
  4. package/src/Message.res.mjs +21 -4
  5. package/src/Projection.res +65 -46
  6. package/src/Projection.res.mjs +53 -49
  7. package/src/components/Aggregate/Aggregate_Callback.res +207 -6
  8. package/src/components/Aggregate/Aggregate_Callback.res.mjs +160 -10
  9. package/src/components/DcbEventLog/DcbEventLog_Operations.res +3 -0
  10. package/src/components/DcbEventLog/DcbEventLog_Operations.res.mjs +3 -1
  11. package/src/components/EventLog/EventLog.res +20 -1
  12. package/src/components/EventLog/EventLog_Adapter.res +3 -1
  13. package/src/components/EventLog/EventLog_Builder.res +4 -0
  14. package/src/components/EventLog/EventLog_Builder.res.mjs +3 -1
  15. package/src/components/EventLog/EventLog_Operations.res +11 -2
  16. package/src/components/EventLog/EventLog_Operations.res.mjs +6 -2
  17. package/src/components/EventLog/EventPublish_Callback.res +7 -0
  18. package/src/components/StateChangeSlice/StateChangeSlice_Callback.res +26 -6
  19. package/src/components/StateChangeSlice/StateChangeSlice_Callback.res.mjs +21 -6
  20. package/src/plugin/lifecycle/PluginBehavior.res +6 -0
  21. package/src/plugin/lifecycle/PluginBehavior.res.mjs +3 -0
  22. package/src/util/Logger.res +10 -0
  23. package/src/util/Logger.res.mjs +12 -2
  24. package/tests/aggregate/AggregateCacheTest.res +271 -0
  25. package/tests/aggregate/AggregateCacheTest.res.mjs +433 -0
  26. package/tests/aggregate/AggregateCallbackTest.res +4 -1
  27. package/tests/aggregate/AggregateCallbackTest.res.mjs +4 -1
  28. package/tests/aggregate/AggregateCausationTest.res +4 -1
  29. package/tests/aggregate/AggregateCausationTest.res.mjs +4 -1
  30. package/tests/aggregate/AggregateConflictTest.res +14 -3
  31. package/tests/aggregate/AggregateConflictTest.res.mjs +26 -3
  32. package/tests/aggregate/AggregateFixtures.res +10 -2
  33. package/tests/aggregate/AggregateFixtures.res.mjs +22 -2
  34. package/tests/aggregate/AggregateRejectionTest.res +4 -1
  35. package/tests/aggregate/AggregateRejectionTest.res.mjs +4 -1
  36. package/tests/aggregate/AggregateSnapshotTest.res +357 -0
  37. package/tests/aggregate/AggregateSnapshotTest.res.mjs +645 -0
  38. package/tests/eventlog/EventLogFixtures.res +13 -2
  39. package/tests/eventlog/EventLogFixtures.res.mjs +27 -3
  40. package/tests/logger/LoggerLazyTest.res +36 -0
  41. package/tests/logger/LoggerLazyTest.res.mjs +40 -0
@@ -0,0 +1,433 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as S from "sury/src/S.res.mjs";
4
+ import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
5
+ import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
6
+ import * as Effect from "effect/Effect";
7
+ import * as Stream from "effect/Stream";
8
+ import * as Aggregate_Callback$ReventlessCore from "../../src/components/Aggregate/Aggregate_Callback.res.mjs";
9
+
10
+ S.enableJson();
11
+
12
+ let name = "CacheTestAggregate";
13
+
14
+ let commandSchema = S.union([
15
+ S.schema(s => ({
16
+ TAG: "Add",
17
+ name: s.m(S.string)
18
+ })),
19
+ S.literal("Checkpoint"),
20
+ S.literal("Noop")
21
+ ]);
22
+
23
+ let eventSchema = S.union([
24
+ S.schema(s => ({
25
+ TAG: "Added",
26
+ name: s.m(S.string)
27
+ })),
28
+ S.schema(s => ({
29
+ TAG: "CheckpointTaken",
30
+ count: s.m(S.int)
31
+ }))
32
+ ]);
33
+
34
+ let errorSchema = S.literal("Never");
35
+
36
+ let moduleUrl = import.meta.url;
37
+
38
+ function commandAuthorization(param) {
39
+ return "AllowAuthenticated";
40
+ }
41
+
42
+ let AggSpec = {
43
+ Id: undefined,
44
+ name: name,
45
+ commandSchema: commandSchema,
46
+ eventSchema: eventSchema,
47
+ errorSchema: errorSchema,
48
+ moduleUrl: moduleUrl,
49
+ commandAuthorization: commandAuthorization
50
+ };
51
+
52
+ let initialState = {
53
+ names: []
54
+ };
55
+
56
+ let moduleUrl$1 = import.meta.url;
57
+
58
+ function evolve(state, event) {
59
+ if (event.TAG === "Added") {
60
+ return {
61
+ names: state.names.concat([event.name])
62
+ };
63
+ } else {
64
+ return state;
65
+ }
66
+ }
67
+
68
+ function decide(state, command) {
69
+ if (typeof command !== "object") {
70
+ if (command === "Checkpoint") {
71
+ return {
72
+ TAG: "Ok",
73
+ _0: [{
74
+ TAG: "CheckpointTaken",
75
+ count: state.names.length
76
+ }]
77
+ };
78
+ } else {
79
+ return {
80
+ TAG: "Ok",
81
+ _0: []
82
+ };
83
+ }
84
+ } else {
85
+ return {
86
+ TAG: "Ok",
87
+ _0: [{
88
+ TAG: "Added",
89
+ name: command.name
90
+ }]
91
+ };
92
+ }
93
+ }
94
+
95
+ let TestBehavior = {
96
+ Spec: undefined,
97
+ initialState: initialState,
98
+ snapshot: undefined,
99
+ moduleUrl: moduleUrl$1,
100
+ evolve: evolve,
101
+ decide: decide
102
+ };
103
+
104
+ let testMeta = {
105
+ service: "CacheTestAggregate",
106
+ time: "2024-01-01T00:00:00Z",
107
+ ip: "127.0.0.1",
108
+ user: "test-user",
109
+ msgId: "msg-1",
110
+ correlationId: "corr-1"
111
+ };
112
+
113
+ function makeMockEL() {
114
+ let storedRef = {
115
+ contents: []
116
+ };
117
+ let appendCount = {
118
+ contents: 0
119
+ };
120
+ let replayCount = {
121
+ contents: 0
122
+ };
123
+ let eventsFor = id => storedRef.contents.filter(e => e.id === id);
124
+ let appendFn = async (seqNr, id, newEvents) => {
125
+ appendCount.contents = appendCount.contents + 1 | 0;
126
+ if (seqNr !== eventsFor(id).length) {
127
+ return {
128
+ TAG: "Error",
129
+ _0: "Conflict"
130
+ };
131
+ } else {
132
+ storedRef.contents = storedRef.contents.concat(newEvents);
133
+ return {
134
+ TAG: "Ok",
135
+ _0: undefined
136
+ };
137
+ }
138
+ };
139
+ let replayFn = async id => {
140
+ replayCount.contents = replayCount.contents + 1 | 0;
141
+ return eventsFor(id).map(e => e.event);
142
+ };
143
+ let replayStreamFn = id => {
144
+ replayCount.contents = replayCount.contents + 1 | 0;
145
+ return Stream.fromIterable(eventsFor(id).map(e => e.event));
146
+ };
147
+ return {
148
+ appendFn: appendFn,
149
+ replayFn: replayFn,
150
+ replayStreamFn: replayStreamFn,
151
+ getAll: () => storedRef.contents,
152
+ injectExternal: (id, event) => {
153
+ storedRef.contents = storedRef.contents.concat([{
154
+ id: id,
155
+ meta: testMeta,
156
+ event: event
157
+ }]);
158
+ },
159
+ appendCallCount: appendCount,
160
+ replayCallCount: replayCount,
161
+ reset: () => {
162
+ storedRef.contents = [];
163
+ appendCount.contents = 0;
164
+ replayCount.contents = 0;
165
+ }
166
+ };
167
+ }
168
+
169
+ let mock = makeMockEL();
170
+
171
+ let name$1 = "CacheMockEventLog";
172
+
173
+ let Spec = {
174
+ Id: undefined,
175
+ name: name$1,
176
+ eventSchema: eventSchema
177
+ };
178
+
179
+ function make(param, param$1) {
180
+ return 0;
181
+ }
182
+
183
+ let EventLog = {
184
+ Spec: Spec,
185
+ make: make
186
+ };
187
+
188
+ let eventLog_append = mock.appendFn;
189
+
190
+ let eventLog_replay = mock.replayFn;
191
+
192
+ function eventLog_replayStream(id, param) {
193
+ return mock.replayStreamFn(id);
194
+ }
195
+
196
+ function eventLog_appendStream(_startingSeqNr, _id, stream) {
197
+ return Stream.runDrain(stream);
198
+ }
199
+
200
+ async function eventLog_latestSnapshot(param) {
201
+ return {
202
+ TAG: "Ok",
203
+ _0: undefined
204
+ };
205
+ }
206
+
207
+ async function eventLog_writeSnapshot(param, param$1) {
208
+ return {
209
+ TAG: "Ok",
210
+ _0: undefined
211
+ };
212
+ }
213
+
214
+ let eventLog = {
215
+ append: eventLog_append,
216
+ replay: eventLog_replay,
217
+ replayStream: eventLog_replayStream,
218
+ appendStream: eventLog_appendStream,
219
+ latestSnapshot: eventLog_latestSnapshot,
220
+ writeSnapshot: eventLog_writeSnapshot
221
+ };
222
+
223
+ let TestOps = {
224
+ Spec: undefined,
225
+ EventLog: EventLog,
226
+ eventLog: eventLog
227
+ };
228
+
229
+ let TestHandler = Aggregate_Callback$ReventlessCore.Make({
230
+ Id: {
231
+ schema: Id$Reventless.StringPure.schema,
232
+ make: prim => prim,
233
+ makeFromString: prim => prim,
234
+ toString: prim => prim,
235
+ cmp: Id$Reventless.StringPure.cmp
236
+ },
237
+ name: name,
238
+ eventSchema: eventSchema,
239
+ errorSchema: errorSchema,
240
+ commandSchema: commandSchema,
241
+ moduleUrl: moduleUrl,
242
+ commandAuthorization: commandAuthorization
243
+ })({
244
+ initialState: initialState,
245
+ evolve: evolve,
246
+ decide: decide,
247
+ snapshot: undefined,
248
+ moduleUrl: moduleUrl$1
249
+ })({
250
+ Spec: {
251
+ Id: {
252
+ schema: Id$Reventless.StringPure.schema,
253
+ make: prim => prim,
254
+ makeFromString: prim => prim,
255
+ toString: prim => prim,
256
+ cmp: Id$Reventless.StringPure.cmp
257
+ },
258
+ name: name,
259
+ eventSchema: eventSchema,
260
+ errorSchema: errorSchema,
261
+ commandSchema: commandSchema,
262
+ moduleUrl: moduleUrl,
263
+ commandAuthorization: commandAuthorization
264
+ },
265
+ EventLog: {
266
+ Spec: {
267
+ Id: {
268
+ schema: Id$Reventless.StringPure.schema,
269
+ make: prim => prim,
270
+ makeFromString: prim => prim,
271
+ toString: prim => prim,
272
+ cmp: Id$Reventless.StringPure.cmp
273
+ },
274
+ name: name$1,
275
+ eventSchema: eventSchema
276
+ },
277
+ make: make
278
+ },
279
+ eventLog: eventLog
280
+ });
281
+
282
+ function makeTopicItem(reference, command) {
283
+ return {
284
+ command: {
285
+ id: "agg-1",
286
+ meta: testMeta,
287
+ command: command
288
+ },
289
+ reference: reference
290
+ };
291
+ }
292
+
293
+ async function run(command) {
294
+ return await Effect.runPromise(TestHandler.handleCommands(Stream.fromIterable([makeTopicItem("ref", command)])));
295
+ }
296
+
297
+ globalThis.beforeEach(() => {
298
+ mock.reset();
299
+ TestHandler.resetCache();
300
+ });
301
+
302
+ globalThis.describe("Aggregate_Callback — replay cache:", () => {
303
+ globalThis.test("warm same-id command skips the replay", async () => {
304
+ let r1 = await run({
305
+ TAG: "Add",
306
+ name: "a"
307
+ });
308
+ let r2 = await run({
309
+ TAG: "Add",
310
+ name: "b"
311
+ });
312
+ globalThis.expect([
313
+ r1,
314
+ r2
315
+ ]).toEqual([
316
+ [{
317
+ TAG: "Ok",
318
+ _0: "ref"
319
+ }],
320
+ [{
321
+ TAG: "Ok",
322
+ _0: "ref"
323
+ }]
324
+ ]);
325
+ globalThis.expect(mock.replayCallCount.contents).toBe(1);
326
+ globalThis.expect([
327
+ mock.appendCallCount.contents,
328
+ mock.getAll().length
329
+ ]).toEqual([
330
+ 2,
331
+ 2
332
+ ]);
333
+ });
334
+ globalThis.test("cached fold state equals a cold replay of the log", async () => {
335
+ await run({
336
+ TAG: "Add",
337
+ name: "a"
338
+ });
339
+ await run({
340
+ TAG: "Add",
341
+ name: "b"
342
+ });
343
+ await run({
344
+ TAG: "Add",
345
+ name: "c"
346
+ });
347
+ await run("Checkpoint");
348
+ globalThis.expect(mock.replayCallCount.contents).toBe(1);
349
+ TestHandler.resetCache();
350
+ await run("Checkpoint");
351
+ globalThis.expect(mock.replayCallCount.contents).toBe(2);
352
+ let checkpoints = Stdlib_Array.filterMap(mock.getAll(), e => {
353
+ let match = e.event;
354
+ if (match.TAG === "Added") {
355
+ return;
356
+ } else {
357
+ return match.count;
358
+ }
359
+ });
360
+ globalThis.expect(checkpoints).toEqual([
361
+ 3,
362
+ 3
363
+ ]);
364
+ });
365
+ globalThis.test("stale cache conflicts, invalidates, and self-heals", async () => {
366
+ await run({
367
+ TAG: "Add",
368
+ name: "a"
369
+ });
370
+ mock.injectExternal("agg-1", {
371
+ TAG: "Added",
372
+ name: "external"
373
+ });
374
+ let results = await run({
375
+ TAG: "Add",
376
+ name: "b"
377
+ });
378
+ globalThis.expect(results).toEqual([{
379
+ TAG: "Ok",
380
+ _0: "ref"
381
+ }]);
382
+ globalThis.expect(mock.replayCallCount.contents).toBe(2);
383
+ globalThis.expect(mock.getAll().length).toBe(3);
384
+ await run("Checkpoint");
385
+ let last = mock.getAll()[3];
386
+ globalThis.expect(last.event).toEqual({
387
+ TAG: "CheckpointTaken",
388
+ count: 3
389
+ });
390
+ });
391
+ globalThis.test("Ok([]) keeps the read snapshot warm", async () => {
392
+ await run({
393
+ TAG: "Add",
394
+ name: "a"
395
+ });
396
+ await run("Noop");
397
+ await run({
398
+ TAG: "Add",
399
+ name: "b"
400
+ });
401
+ globalThis.expect(mock.replayCallCount.contents).toBe(1);
402
+ globalThis.expect(mock.getAll().length).toBe(2);
403
+ });
404
+ globalThis.test("resetCache forces the next command to replay cold", async () => {
405
+ await run({
406
+ TAG: "Add",
407
+ name: "a"
408
+ });
409
+ TestHandler.resetCache();
410
+ await run({
411
+ TAG: "Add",
412
+ name: "b"
413
+ });
414
+ globalThis.expect(mock.replayCallCount.contents).toBe(2);
415
+ globalThis.expect(mock.getAll().length).toBe(2);
416
+ });
417
+ });
418
+
419
+ let OuterEventLog;
420
+
421
+ export {
422
+ AggSpec,
423
+ TestBehavior,
424
+ testMeta,
425
+ makeMockEL,
426
+ mock,
427
+ OuterEventLog,
428
+ TestOps,
429
+ TestHandler,
430
+ makeTopicItem,
431
+ run,
432
+ }
433
+ /* Not a pure module */
@@ -1,7 +1,10 @@
1
1
  open JestGlobals
2
2
  open AggregateFixtures
3
3
 
4
- let _ = beforeEach(() => mock.reset())
4
+ let _ = beforeEach(() => {
5
+ mock.reset()
6
+ TestHandler.resetCache()
7
+ })
5
8
 
6
9
  describe("Aggregate_Callback.handleCommands:", () => {
7
10
  describe("single command — new aggregate", () => {
@@ -4,7 +4,10 @@ import * as Effect from "effect/Effect";
4
4
  import * as Stream from "effect/Stream";
5
5
  import * as AggregateFixtures$ReventlessCore from "./AggregateFixtures.res.mjs";
6
6
 
7
- globalThis.beforeEach(() => AggregateFixtures$ReventlessCore.mock.reset());
7
+ globalThis.beforeEach(() => {
8
+ AggregateFixtures$ReventlessCore.mock.reset();
9
+ AggregateFixtures$ReventlessCore.TestHandler.resetCache();
10
+ });
8
11
 
9
12
  globalThis.describe("Aggregate_Callback.handleCommands:", () => {
10
13
  globalThis.describe("single command — new aggregate", () => {
@@ -8,7 +8,10 @@
8
8
  open JestGlobals
9
9
  open AggregateFixtures
10
10
 
11
- let _ = beforeEach(() => mock.reset())
11
+ let _ = beforeEach(() => {
12
+ mock.reset()
13
+ TestHandler.resetCache()
14
+ })
12
15
 
13
16
  describe("Aggregate_Callback causation propagation:", () => {
14
17
  testPromise("event.meta.causationId = command.meta.msgId", async () => {
@@ -4,7 +4,10 @@ import * as Effect from "effect/Effect";
4
4
  import * as Stream from "effect/Stream";
5
5
  import * as AggregateFixtures$ReventlessCore from "./AggregateFixtures.res.mjs";
6
6
 
7
- globalThis.beforeEach(() => AggregateFixtures$ReventlessCore.mock.reset());
7
+ globalThis.beforeEach(() => {
8
+ AggregateFixtures$ReventlessCore.mock.reset();
9
+ AggregateFixtures$ReventlessCore.TestHandler.resetCache();
10
+ });
8
11
 
9
12
  globalThis.describe("Aggregate_Callback causation propagation:", () => {
10
13
  globalThis.test("event.meta.causationId = command.meta.msgId", async () => {
@@ -27,6 +27,10 @@ module TestBehavior = {
27
27
 
28
28
  let initialState = {name: ""}
29
29
 
30
+ // Hand-written behavior (no @@reventless.behavior PPX) — satisfy the
31
+ // Behavior.T snapshot field manually.
32
+ let snapshot = None
33
+
30
34
  let moduleUrl: string = %raw(`import.meta.url`)
31
35
 
32
36
  let evolve = (_state: state, event: AggSpec.event): state =>
@@ -116,8 +120,10 @@ module TestOps = {
116
120
  type operations = {
117
121
  append: (int, string, array<Message.event'<string, AggSpec.event>>) => promise<result<unit, EventLog.appendError>>,
118
122
  replay: string => promise<array<AggSpec.event>>,
119
- replayStream: string => Stream.t<AggSpec.event, string, unit>,
123
+ replayStream: (string, ~fromSeq: int=?) => Stream.t<AggSpec.event, string, unit>,
120
124
  appendStream: (int, string, Stream.t<AggSpec.event, string, unit>) => Effect.t<unit, string, unit>,
125
+ latestSnapshot: string => promise<result<option<EventLog.snapshot>, string>>,
126
+ writeSnapshot: (string, EventLog.snapshot) => promise<result<unit, string>>,
121
127
  }
122
128
  type component = Component.t<OuterEventLog.t, OuterEventLog.outputs, operations>
123
129
  let make = (~name as _: string, ~opts as _=?): component => Obj.magic(0)
@@ -125,8 +131,10 @@ module TestOps = {
125
131
  let eventLog: EventLog.operations = {
126
132
  append: mock.appendFn,
127
133
  replay: mock.replayFn,
128
- replayStream: mock.replayStreamFn,
134
+ replayStream: (id, ~fromSeq as _=?) => mock.replayStreamFn(id),
129
135
  appendStream: (_startingSeqNr, _id, stream) => stream->Stream.runDrain,
136
+ latestSnapshot: async _ => Ok(None),
137
+ writeSnapshot: async (_, _) => Ok(),
130
138
  }
131
139
  }
132
140
 
@@ -152,7 +160,10 @@ let makeTopicItem = (reference, command): CommandTopic.topicItem<
152
160
  reference,
153
161
  }
154
162
 
155
- let _ = beforeEach(() => mock.reset())
163
+ let _ = beforeEach(() => {
164
+ mock.reset()
165
+ TestHandler.resetCache()
166
+ })
156
167
 
157
168
  describe("Aggregate_Callback — conflict retry:", () => {
158
169
  testPromise("1 conflict retries and succeeds", async () => {
@@ -63,6 +63,7 @@ function decide(_state, command) {
63
63
  let TestBehavior = {
64
64
  Spec: undefined,
65
65
  initialState: initialState,
66
+ snapshot: undefined,
66
67
  moduleUrl: moduleUrl$1,
67
68
  evolve: evolve,
68
69
  decide: decide
@@ -145,17 +146,35 @@ let eventLog_append = mock.appendFn;
145
146
 
146
147
  let eventLog_replay = mock.replayFn;
147
148
 
148
- let eventLog_replayStream = mock.replayStreamFn;
149
+ function eventLog_replayStream(id, param) {
150
+ return mock.replayStreamFn(id);
151
+ }
149
152
 
150
153
  function eventLog_appendStream(_startingSeqNr, _id, stream) {
151
154
  return Stream.runDrain(stream);
152
155
  }
153
156
 
157
+ async function eventLog_latestSnapshot(param) {
158
+ return {
159
+ TAG: "Ok",
160
+ _0: undefined
161
+ };
162
+ }
163
+
164
+ async function eventLog_writeSnapshot(param, param$1) {
165
+ return {
166
+ TAG: "Ok",
167
+ _0: undefined
168
+ };
169
+ }
170
+
154
171
  let eventLog = {
155
172
  append: eventLog_append,
156
173
  replay: eventLog_replay,
157
174
  replayStream: eventLog_replayStream,
158
- appendStream: eventLog_appendStream
175
+ appendStream: eventLog_appendStream,
176
+ latestSnapshot: eventLog_latestSnapshot,
177
+ writeSnapshot: eventLog_writeSnapshot
159
178
  };
160
179
 
161
180
  let TestOps = {
@@ -182,6 +201,7 @@ let TestHandler = Aggregate_Callback$ReventlessCore.Make({
182
201
  initialState: initialState,
183
202
  evolve: evolve,
184
203
  decide: decide,
204
+ snapshot: undefined,
185
205
  moduleUrl: moduleUrl$1
186
206
  })({
187
207
  Spec: {
@@ -236,7 +256,10 @@ function makeTopicItem(reference, command) {
236
256
  };
237
257
  }
238
258
 
239
- globalThis.beforeEach(() => mock.reset());
259
+ globalThis.beforeEach(() => {
260
+ mock.reset();
261
+ TestHandler.resetCache();
262
+ });
240
263
 
241
264
  globalThis.describe("Aggregate_Callback — conflict retry:", () => {
242
265
  globalThis.test("1 conflict retries and succeeds", async () => {
@@ -38,6 +38,10 @@ module TestBehavior = {
38
38
 
39
39
  let initialState = NotCreated
40
40
 
41
+ // Hand-written behavior (no @@reventless.behavior PPX) — satisfy the
42
+ // Behavior.T snapshot field manually.
43
+ let snapshot = None
44
+
41
45
  let moduleUrl: string = %raw(`import.meta.url`)
42
46
 
43
47
  let evolve = (_state: state, event: AggSpec.event): state =>
@@ -131,8 +135,10 @@ module TestOps = {
131
135
  array<Message.event'<string, AggSpec.event>>,
132
136
  ) => promise<result<unit, EventLog.appendError>>,
133
137
  replay: string => promise<array<AggSpec.event>>,
134
- replayStream: string => Stream.t<AggSpec.event, string, unit>,
138
+ replayStream: (string, ~fromSeq: int=?) => Stream.t<AggSpec.event, string, unit>,
135
139
  appendStream: (int, string, Stream.t<AggSpec.event, string, unit>) => Effect.t<unit, string, unit>,
140
+ latestSnapshot: string => promise<result<option<EventLog.snapshot>, string>>,
141
+ writeSnapshot: (string, EventLog.snapshot) => promise<result<unit, string>>,
136
142
  }
137
143
  type component = Component.t<OuterEventLog.t, OuterEventLog.outputs, operations>
138
144
  // Never called — satisfies module type only
@@ -141,8 +147,10 @@ module TestOps = {
141
147
  let eventLog: EventLog.operations = {
142
148
  append: mock.appendFn,
143
149
  replay: mock.replayFn,
144
- replayStream: mock.replayStreamFn,
150
+ replayStream: (id, ~fromSeq as _=?) => mock.replayStreamFn(id),
145
151
  appendStream: (_startingSeqNr, _id, stream) => stream->Stream.runDrain,
152
+ latestSnapshot: async _ => Ok(None),
153
+ writeSnapshot: async (_, _) => Ok(),
146
154
  }
147
155
  }
148
156
 
@@ -112,6 +112,7 @@ let TestBehavior = {
112
112
  Spec: undefined,
113
113
  stateSchema: stateSchema,
114
114
  initialState: "NotCreated",
115
+ snapshot: undefined,
115
116
  moduleUrl: moduleUrl$1,
116
117
  evolve: evolve,
117
118
  decide: decide
@@ -180,17 +181,35 @@ let eventLog_append = mock.appendFn;
180
181
 
181
182
  let eventLog_replay = mock.replayFn;
182
183
 
183
- let eventLog_replayStream = mock.replayStreamFn;
184
+ function eventLog_replayStream(id, param) {
185
+ return mock.replayStreamFn(id);
186
+ }
184
187
 
185
188
  function eventLog_appendStream(_startingSeqNr, _id, stream) {
186
189
  return Stream.runDrain(stream);
187
190
  }
188
191
 
192
+ async function eventLog_latestSnapshot(param) {
193
+ return {
194
+ TAG: "Ok",
195
+ _0: undefined
196
+ };
197
+ }
198
+
199
+ async function eventLog_writeSnapshot(param, param$1) {
200
+ return {
201
+ TAG: "Ok",
202
+ _0: undefined
203
+ };
204
+ }
205
+
189
206
  let eventLog = {
190
207
  append: eventLog_append,
191
208
  replay: eventLog_replay,
192
209
  replayStream: eventLog_replayStream,
193
- appendStream: eventLog_appendStream
210
+ appendStream: eventLog_appendStream,
211
+ latestSnapshot: eventLog_latestSnapshot,
212
+ writeSnapshot: eventLog_writeSnapshot
194
213
  };
195
214
 
196
215
  let TestOps = {
@@ -217,6 +236,7 @@ let TestHandler = Aggregate_Callback$ReventlessCore.Make({
217
236
  initialState: "NotCreated",
218
237
  evolve: evolve,
219
238
  decide: decide,
239
+ snapshot: undefined,
220
240
  moduleUrl: moduleUrl$1
221
241
  })({
222
242
  Spec: {
@@ -7,7 +7,10 @@
7
7
  open JestGlobals
8
8
  open AggregateFixtures
9
9
 
10
- let _ = beforeEach(() => mock.reset())
10
+ let _ = beforeEach(() => {
11
+ mock.reset()
12
+ TestHandler.resetCache()
13
+ })
11
14
 
12
15
  // Captures whatever flows through CommandTopic_Helpers.reportRejected during a test.
13
16
  let capturedRejections: ref<array<(string, CommandTopic_Helpers.rejectedResult)>> = ref([])
@@ -5,7 +5,10 @@ import * as Stream from "effect/Stream";
5
5
  import * as AggregateFixtures$ReventlessCore from "./AggregateFixtures.res.mjs";
6
6
  import * as CommandTopic_Helpers$ReventlessCore from "../../src/components/CommandTopic/CommandTopic_Helpers.res.mjs";
7
7
 
8
- globalThis.beforeEach(() => AggregateFixtures$ReventlessCore.mock.reset());
8
+ globalThis.beforeEach(() => {
9
+ AggregateFixtures$ReventlessCore.mock.reset();
10
+ AggregateFixtures$ReventlessCore.TestHandler.resetCache();
11
+ });
9
12
 
10
13
  let capturedRejections = {
11
14
  contents: []