@reventlessdev/reventless-local 3.0.0-alpha.115 → 3.0.0-alpha.116

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 (59) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/package.json +6 -6
  3. package/src/Platform.res +91 -17
  4. package/src/Platform.res.mjs +89 -30
  5. package/src/adapter/DcbEventLog/DcbEventLogStorage_InMemory.res +102 -17
  6. package/src/adapter/DcbEventLog/DcbEventLogStorage_InMemory.res.mjs +105 -22
  7. package/src/adapter/DcbEventLog/DcbEventLogStorage_Sqlite.res +113 -39
  8. package/src/adapter/DcbEventLog/DcbEventLogStorage_Sqlite.res.mjs +87 -23
  9. package/src/adapter/EventCollector/LocalEventCollectorChannel.res +33 -0
  10. package/src/adapter/EventCollector/LocalEventCollectorChannel.res.mjs +44 -0
  11. package/src/adapter/EventLog/EventLogStorage_InMemory.res +23 -2
  12. package/src/adapter/EventLog/EventLogStorage_InMemory.res.mjs +20 -3
  13. package/src/adapter/EventLog/EventLogStorage_Sqlite.res +132 -24
  14. package/src/adapter/EventLog/EventLogStorage_Sqlite.res.mjs +119 -37
  15. package/src/adapter/LocalBus.res +62 -0
  16. package/src/adapter/LocalBus.res.mjs +120 -0
  17. package/src/adapter/ProjectionCheckpoint.res +350 -0
  18. package/src/adapter/ProjectionCheckpoint.res.mjs +327 -0
  19. package/src/adapter/ProjectionPending.res +60 -0
  20. package/src/adapter/ProjectionPending.res.mjs +64 -0
  21. package/src/adapter/QueryDb/QueryDbListQuery.res +261 -0
  22. package/src/adapter/QueryDb/QueryDbListQuery.res.mjs +212 -0
  23. package/src/adapter/QueryDb/QueryDbResolvers_GraphQL.res +54 -286
  24. package/src/adapter/QueryDb/QueryDbResolvers_GraphQL.res.mjs +28 -190
  25. package/src/adapter/QueryDb/QueryDbStorage_InMemory.res +32 -6
  26. package/src/adapter/QueryDb/QueryDbStorage_InMemory.res.mjs +30 -14
  27. package/src/adapter/QueryDb/QueryDbStorage_Sqlite.res +163 -11
  28. package/src/adapter/QueryDb/QueryDbStorage_Sqlite.res.mjs +101 -3
  29. package/src/adapter/SqliteDriver.res +11 -1
  30. package/src/adapter/SqliteDriver.res.mjs +5 -1
  31. package/src/components/ReadModel_Builder.res +3 -1
  32. package/src/components/ReadModel_Builder.res.mjs +13 -4
  33. package/src/components/StateViewSlice_Builder.res +4 -1
  34. package/src/components/StateViewSlice_Builder.res.mjs +9 -3
  35. package/src/test/Mocks/MockEventLogStorage.res +19 -2
  36. package/src/test/Mocks/MockEventLogStorage.res.mjs +21 -2
  37. package/tests/PluginEventDecodeTest.res +51 -0
  38. package/tests/PluginEventDecodeTest.res.mjs +63 -0
  39. package/tests/adapter/DcbEventLogStorageSqliteTest.res +73 -0
  40. package/tests/adapter/DcbEventLogStorageSqliteTest.res.mjs +86 -0
  41. package/tests/adapter/DcbEventLogStorageTest.res +46 -0
  42. package/tests/adapter/DcbEventLogStorageTest.res.mjs +63 -0
  43. package/tests/adapter/EventLogSnapshotParityTest.res +81 -0
  44. package/tests/adapter/EventLogSnapshotParityTest.res.mjs +141 -0
  45. package/tests/adapter/EventLogStorageSqliteTest.res +128 -0
  46. package/tests/adapter/EventLogStorageSqliteTest.res.mjs +189 -0
  47. package/tests/adapter/ProjectionCheckpointTest.res +422 -0
  48. package/tests/adapter/ProjectionCheckpointTest.res.mjs +401 -0
  49. package/tests/adapter/QueryDbGsiTtlTest.res +78 -0
  50. package/tests/adapter/QueryDbGsiTtlTest.res.mjs +77 -0
  51. package/tests/adapter/QueryDbListPushdownParityTest.res +197 -0
  52. package/tests/adapter/QueryDbListPushdownParityTest.res.mjs +292 -0
  53. package/tests/adapter/QueryDbListResolverTest.res +76 -0
  54. package/tests/adapter/QueryDbListResolverTest.res.mjs +84 -0
  55. package/tests/components/aggregate/AggregateFixtures.res +4 -0
  56. package/tests/components/aggregate/AggregateFixtures.res.mjs +1 -0
  57. package/tests/components/eventlog/EventLogAppendStreamTest.res.mjs +4 -4
  58. package/tests/components/eventlog/EventLogStreamTest.res.mjs +8 -8
  59. package/tests/plugin/PluginBehavior_GWT.res.mjs +1 -0
@@ -0,0 +1,141 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Stream from "@reventlessdev/rescript-effect/src/Stream.res.mjs";
4
+ import * as Effect from "effect/Effect";
5
+ import * as LocalBus$ReventlessLocal from "../../src/adapter/LocalBus.res.mjs";
6
+ import * as TestRunner$ReventlessLocal from "../../src/test/TestRunner.res.mjs";
7
+ import * as SqliteDriver$ReventlessLocal from "../../src/adapter/SqliteDriver.res.mjs";
8
+ import * as EventLogStorage_Sqlite$ReventlessLocal from "../../src/adapter/EventLog/EventLogStorage_Sqlite.res.mjs";
9
+ import * as EventLogStorage_InMemory$ReventlessLocal from "../../src/adapter/EventLog/EventLogStorage_InMemory.res.mjs";
10
+
11
+ TestRunner$ReventlessLocal.setup();
12
+
13
+ let opts = {};
14
+
15
+ function makeMemoryOps() {
16
+ let s = EventLogStorage_InMemory$ReventlessLocal.make("parity", opts);
17
+ return TestRunner$ReventlessLocal.resolve(s.operations);
18
+ }
19
+
20
+ function makeSqliteOps() {
21
+ let TestBus = LocalBus$ReventlessLocal.Make({});
22
+ let db = SqliteDriver$ReventlessLocal.openDb(":memory:");
23
+ let DbProvider = {
24
+ db: db
25
+ };
26
+ let Storage = EventLogStorage_Sqlite$ReventlessLocal.Make(TestBus)(DbProvider);
27
+ let s = Storage.make("parity", opts);
28
+ return TestRunner$ReventlessLocal.resolve(s.operations);
29
+ }
30
+
31
+ let backends = [
32
+ [
33
+ "memory",
34
+ makeMemoryOps
35
+ ],
36
+ [
37
+ "sqlite",
38
+ makeSqliteOps
39
+ ]
40
+ ];
41
+
42
+ globalThis.describe("EventLog snapshot backend parity", () => {
43
+ backends.forEach(param => {
44
+ let makeOps = param[1];
45
+ let label = param[0];
46
+ globalThis.test(label + `: latestSnapshot is None before any write`, async () => {
47
+ let ops = await makeOps();
48
+ globalThis.expect(await ops.latestSnapshot("id-1")).toEqual({
49
+ TAG: "Ok",
50
+ _0: undefined
51
+ });
52
+ });
53
+ globalThis.test(label + `: snapshot round-trips and keep-one overwrites`, async () => {
54
+ let ops = await makeOps();
55
+ let state = Object.fromEntries([[
56
+ "n",
57
+ 1
58
+ ]]);
59
+ globalThis.expect(await ops.writeSnapshot("id-1", {
60
+ seqNr: 2,
61
+ state: state,
62
+ schemaHash: "h1"
63
+ })).toEqual({
64
+ TAG: "Ok",
65
+ _0: undefined
66
+ });
67
+ globalThis.expect(await ops.latestSnapshot("id-1")).toEqual({
68
+ TAG: "Ok",
69
+ _0: {
70
+ seqNr: 2,
71
+ state: state,
72
+ schemaHash: "h1"
73
+ }
74
+ });
75
+ let state2 = Object.fromEntries([[
76
+ "n",
77
+ 9
78
+ ]]);
79
+ await ops.writeSnapshot("id-1", {
80
+ seqNr: 9,
81
+ state: state2,
82
+ schemaHash: "h2"
83
+ });
84
+ globalThis.expect(await ops.latestSnapshot("id-1")).toEqual({
85
+ TAG: "Ok",
86
+ _0: {
87
+ seqNr: 9,
88
+ state: state2,
89
+ schemaHash: "h2"
90
+ }
91
+ });
92
+ });
93
+ globalThis.test(label + `: snapshots are per-aggregate-id`, async () => {
94
+ let ops = await makeOps();
95
+ let state = Object.fromEntries([[
96
+ "n",
97
+ 1
98
+ ]]);
99
+ await ops.writeSnapshot("id-a", {
100
+ seqNr: 1,
101
+ state: state,
102
+ schemaHash: "h"
103
+ });
104
+ globalThis.expect(await ops.latestSnapshot("id-b")).toEqual({
105
+ TAG: "Ok",
106
+ _0: undefined
107
+ });
108
+ });
109
+ globalThis.test(label + `: replayStream ~fromSeq equals the tail of a full replay`, async () => {
110
+ let ops = await makeOps();
111
+ let ev = i => Object.fromEntries([[
112
+ "i",
113
+ i
114
+ ]]);
115
+ await ops.append(0, "id-1", [
116
+ ev(0),
117
+ ev(1),
118
+ ev(2),
119
+ ev(3),
120
+ ev(4)
121
+ ]);
122
+ let delta = await Effect.runPromise(Stream.runCollect(ops.replayStream("id-1", 3)));
123
+ globalThis.expect(delta).toEqual([
124
+ ev(3),
125
+ ev(4)
126
+ ]);
127
+ let empty = await Effect.runPromise(Stream.runCollect(ops.replayStream("id-1", 5)));
128
+ globalThis.expect(empty).toEqual([]);
129
+ let full = await Effect.runPromise(Stream.runCollect(ops.replayStream("id-1", undefined)));
130
+ globalThis.expect(full.length).toBe(5);
131
+ });
132
+ });
133
+ });
134
+
135
+ export {
136
+ opts,
137
+ makeMemoryOps,
138
+ makeSqliteOps,
139
+ backends,
140
+ }
141
+ /* Not a pure module */
@@ -61,6 +61,52 @@ describe("EventLogStorage_Sqlite", () => {
61
61
  }
62
62
  })
63
63
 
64
+ testPromise("multi-event append advances the expected seq via MAX(seq_nr)+1", async () => {
65
+ module TestBus = LocalBus.Make()
66
+ module DbProvider = {
67
+ let db = makeFreshDb()
68
+ }
69
+ module Storage = EventLogStorage_Sqlite.Make(TestBus, DbProvider)
70
+ let s = Storage.make(~name="agg", ~opts)
71
+ let ops = await s.operations->TestRunner.resolve
72
+
73
+ let ev = i => JSON.Encode.object(Dict.fromArray([("i", JSON.Encode.int(i))]))
74
+ // Batch of three at seq 0 → occupies seq 0,1,2.
75
+ let r1 = await ops.append(0, "id-m", [ev(0), ev(1), ev(2)])
76
+ expect(r1)->toEqual(Ok())
77
+ // A stale seq (2) is a conflict; the correct next seq is 3.
78
+ let stale = await ops.append(2, "id-m", [ev(9)])
79
+ switch stale {
80
+ | Error(ReventlessCore.EventLog.Conflict) => expect(true)->toBe(true)
81
+ | _ => expect("expected Conflict")->toBe("other")
82
+ }
83
+ let r2 = await ops.append(3, "id-m", [ev(3)])
84
+ expect(r2)->toEqual(Ok())
85
+ let replayed = await ops.replay("id-m")
86
+ expect(replayed->Array.length)->toBe(4)
87
+ })
88
+
89
+ testPromise("appendStream persists the whole batch in order from the starting seq", async () => {
90
+ module TestBus = LocalBus.Make()
91
+ module DbProvider = {
92
+ let db = makeFreshDb()
93
+ }
94
+ module Storage = EventLogStorage_Sqlite.Make(TestBus, DbProvider)
95
+ let s = Storage.make(~name="agg", ~opts)
96
+ let ops = await s.operations->TestRunner.resolve
97
+
98
+ let ev = i => JSON.Encode.object(Dict.fromArray([("i", JSON.Encode.int(i))]))
99
+ // Seed one event, then stream three more starting at seq 1.
100
+ let _ = await ops.append(0, "id-s", [ev(0)])
101
+ let _ =
102
+ await ops.appendStream(1, "id-s", Stream.fromIterable([ev(1), ev(2), ev(3)]))
103
+ ->Effect.runPromise
104
+ let replayed = await ops.replay("id-s")
105
+ expect(replayed->Array.length)->toBe(4)
106
+ expect(replayed->Array.getUnsafe(0))->toEqual(ev(0))
107
+ expect(replayed->Array.getUnsafe(3))->toEqual(ev(3))
108
+ })
109
+
64
110
  testPromise("replay returns empty array for unknown id", async () => {
65
111
  module TestBus = LocalBus.Make()
66
112
  module DbProvider = {
@@ -75,6 +121,88 @@ describe("EventLogStorage_Sqlite", () => {
75
121
  expect(replayed->Array.length)->toBe(0)
76
122
  })
77
123
 
124
+ testPromise("replayStream ~fromSeq reads only the delta", async () => {
125
+ module TestBus = LocalBus.Make()
126
+ module DbProvider = {
127
+ let db = makeFreshDb()
128
+ }
129
+ module Storage = EventLogStorage_Sqlite.Make(TestBus, DbProvider)
130
+ let s = Storage.make(~name="agg", ~opts)
131
+ let ops = await s.operations->TestRunner.resolve
132
+
133
+ let ev = i => JSON.Encode.object(Dict.fromArray([("i", JSON.Encode.int(i))]))
134
+ let _ = await ops.append(0, "id-d", [ev(0), ev(1), ev(2), ev(3)])
135
+ let delta = await ops.replayStream("id-d", ~fromSeq=2)->Stream.runCollect->Effect.runPromise
136
+ expect(delta)->toEqual([ev(2), ev(3)])
137
+ // Omitted fromSeq still replays from 0 (unchanged default).
138
+ let full = await ops.replayStream("id-d")->Stream.runCollect->Effect.runPromise
139
+ expect(full->Array.length)->toBe(4)
140
+ })
141
+
142
+ testPromise("snapshot round-trip with keep-one overwrite", async () => {
143
+ module TestBus = LocalBus.Make()
144
+ module DbProvider = {
145
+ let db = makeFreshDb()
146
+ }
147
+ module Storage = EventLogStorage_Sqlite.Make(TestBus, DbProvider)
148
+ let s = Storage.make(~name="agg", ~opts)
149
+ let ops = await s.operations->TestRunner.resolve
150
+
151
+ let none = await ops.latestSnapshot("id-snap")
152
+ expect(none)->toEqual(Ok(None))
153
+
154
+ let state = JSON.Encode.object(Dict.fromArray([("names", JSON.Encode.int(2))]))
155
+ let w1 = await ops.writeSnapshot(
156
+ "id-snap",
157
+ {ReventlessCore.EventLog.seqNr: 2, state, schemaHash: "h1"},
158
+ )
159
+ expect(w1)->toEqual(Ok())
160
+ let r1 = await ops.latestSnapshot("id-snap")
161
+ expect(r1)->toEqual(Ok(Some({ReventlessCore.EventLog.seqNr: 2, state, schemaHash: "h1"})))
162
+
163
+ // Keep-one: a later write replaces the row, it does not accumulate.
164
+ let state5 = JSON.Encode.object(Dict.fromArray([("names", JSON.Encode.int(5))]))
165
+ let _ = await ops.writeSnapshot(
166
+ "id-snap",
167
+ {ReventlessCore.EventLog.seqNr: 5, state: state5, schemaHash: "h2"},
168
+ )
169
+ let r2 = await ops.latestSnapshot("id-snap")
170
+ expect(r2)->toEqual(
171
+ Ok(Some({ReventlessCore.EventLog.seqNr: 5, state: state5, schemaHash: "h2"})),
172
+ )
173
+ })
174
+
175
+ testPromise("snapshots persist across a reopen of the database file", async () => {
176
+ let path = `/tmp/reventless-test-snapshot-${Float.toString(Date.now())}.db`
177
+ let state = JSON.Encode.object(Dict.fromArray([("v", JSON.Encode.int(7))]))
178
+
179
+ {
180
+ module TestBus = LocalBus.Make()
181
+ module DbProvider = {
182
+ let db = SqliteDriver.openDb(~path)
183
+ }
184
+ module Storage = EventLogStorage_Sqlite.Make(TestBus, DbProvider)
185
+ let s = Storage.make(~name="persist", ~opts)
186
+ let ops = await s.operations->TestRunner.resolve
187
+ let _ = await ops.writeSnapshot(
188
+ "id-sp",
189
+ {ReventlessCore.EventLog.seqNr: 3, state, schemaHash: "h"},
190
+ )
191
+ DbProvider.db->SqliteDriver.close
192
+ }
193
+
194
+ module TestBus2 = LocalBus.Make()
195
+ module DbProvider2 = {
196
+ let db = SqliteDriver.openDb(~path)
197
+ }
198
+ module Storage2 = EventLogStorage_Sqlite.Make(TestBus2, DbProvider2)
199
+ let s2 = Storage2.make(~name="persist", ~opts)
200
+ let ops2 = await s2.operations->TestRunner.resolve
201
+ let r = await ops2.latestSnapshot("id-sp")
202
+ expect(r)->toEqual(Ok(Some({ReventlessCore.EventLog.seqNr: 3, state, schemaHash: "h"})))
203
+ DbProvider2.db->SqliteDriver.close
204
+ })
205
+
78
206
  testPromise("events persist across a reopen of the database file", async () => {
79
207
  let path = `/tmp/reventless-test-eventlog-${Float.toString(Date.now())}.db`
80
208
 
@@ -1,5 +1,8 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
+ import * as Stream from "@reventlessdev/rescript-effect/src/Stream.res.mjs";
4
+ import * as Effect from "effect/Effect";
5
+ import * as Stream$1 from "effect/Stream";
3
6
  import * as LocalBus$ReventlessLocal from "../../src/adapter/LocalBus.res.mjs";
4
7
  import * as TestRunner$ReventlessLocal from "../../src/test/TestRunner.res.mjs";
5
8
  import * as SqliteDriver$ReventlessLocal from "../../src/adapter/SqliteDriver.res.mjs";
@@ -72,6 +75,71 @@ globalThis.describe("EventLogStorage_Sqlite", () => {
72
75
  }
73
76
  globalThis.expect("expected Conflict, got StorageFailure").toBe(msg._0);
74
77
  });
78
+ globalThis.test("multi-event append advances the expected seq via MAX(seq_nr)+1", async () => {
79
+ let TestBus = LocalBus$ReventlessLocal.Make({});
80
+ let db = SqliteDriver$ReventlessLocal.openDb(":memory:");
81
+ let DbProvider = {
82
+ db: db
83
+ };
84
+ let Storage = EventLogStorage_Sqlite$ReventlessLocal.Make(TestBus)(DbProvider);
85
+ let s = Storage.make("agg", opts);
86
+ let ops = await TestRunner$ReventlessLocal.resolve(s.operations);
87
+ let ev = i => Object.fromEntries([[
88
+ "i",
89
+ i
90
+ ]]);
91
+ let r1 = await ops.append(0, "id-m", [
92
+ ev(0),
93
+ ev(1),
94
+ ev(2)
95
+ ]);
96
+ globalThis.expect(r1).toEqual({
97
+ TAG: "Ok",
98
+ _0: undefined
99
+ });
100
+ let stale = await ops.append(2, "id-m", [ev(9)]);
101
+ if (stale.TAG === "Ok") {
102
+ globalThis.expect("expected Conflict").toBe("other");
103
+ } else {
104
+ let tmp = stale._0;
105
+ if (typeof tmp !== "object") {
106
+ globalThis.expect(true).toBe(true);
107
+ } else {
108
+ globalThis.expect("expected Conflict").toBe("other");
109
+ }
110
+ }
111
+ let r2 = await ops.append(3, "id-m", [ev(3)]);
112
+ globalThis.expect(r2).toEqual({
113
+ TAG: "Ok",
114
+ _0: undefined
115
+ });
116
+ let replayed = await ops.replay("id-m");
117
+ globalThis.expect(replayed.length).toBe(4);
118
+ });
119
+ globalThis.test("appendStream persists the whole batch in order from the starting seq", async () => {
120
+ let TestBus = LocalBus$ReventlessLocal.Make({});
121
+ let db = SqliteDriver$ReventlessLocal.openDb(":memory:");
122
+ let DbProvider = {
123
+ db: db
124
+ };
125
+ let Storage = EventLogStorage_Sqlite$ReventlessLocal.Make(TestBus)(DbProvider);
126
+ let s = Storage.make("agg", opts);
127
+ let ops = await TestRunner$ReventlessLocal.resolve(s.operations);
128
+ let ev = i => Object.fromEntries([[
129
+ "i",
130
+ i
131
+ ]]);
132
+ await ops.append(0, "id-s", [ev(0)]);
133
+ await Effect.runPromise(ops.appendStream(1, "id-s", Stream$1.fromIterable([
134
+ ev(1),
135
+ ev(2),
136
+ ev(3)
137
+ ])));
138
+ let replayed = await ops.replay("id-s");
139
+ globalThis.expect(replayed.length).toBe(4);
140
+ globalThis.expect(replayed[0]).toEqual(ev(0));
141
+ globalThis.expect(replayed[3]).toEqual(ev(3));
142
+ });
75
143
  globalThis.test("replay returns empty array for unknown id", async () => {
76
144
  let TestBus = LocalBus$ReventlessLocal.Make({});
77
145
  let db = SqliteDriver$ReventlessLocal.openDb(":memory:");
@@ -84,6 +152,127 @@ globalThis.describe("EventLogStorage_Sqlite", () => {
84
152
  let replayed = await ops.replay("not-there");
85
153
  globalThis.expect(replayed.length).toBe(0);
86
154
  });
155
+ globalThis.test("replayStream ~fromSeq reads only the delta", async () => {
156
+ let TestBus = LocalBus$ReventlessLocal.Make({});
157
+ let db = SqliteDriver$ReventlessLocal.openDb(":memory:");
158
+ let DbProvider = {
159
+ db: db
160
+ };
161
+ let Storage = EventLogStorage_Sqlite$ReventlessLocal.Make(TestBus)(DbProvider);
162
+ let s = Storage.make("agg", opts);
163
+ let ops = await TestRunner$ReventlessLocal.resolve(s.operations);
164
+ let ev = i => Object.fromEntries([[
165
+ "i",
166
+ i
167
+ ]]);
168
+ await ops.append(0, "id-d", [
169
+ ev(0),
170
+ ev(1),
171
+ ev(2),
172
+ ev(3)
173
+ ]);
174
+ let delta = await Effect.runPromise(Stream.runCollect(ops.replayStream("id-d", 2)));
175
+ globalThis.expect(delta).toEqual([
176
+ ev(2),
177
+ ev(3)
178
+ ]);
179
+ let full = await Effect.runPromise(Stream.runCollect(ops.replayStream("id-d", undefined)));
180
+ globalThis.expect(full.length).toBe(4);
181
+ });
182
+ globalThis.test("snapshot round-trip with keep-one overwrite", async () => {
183
+ let TestBus = LocalBus$ReventlessLocal.Make({});
184
+ let db = SqliteDriver$ReventlessLocal.openDb(":memory:");
185
+ let DbProvider = {
186
+ db: db
187
+ };
188
+ let Storage = EventLogStorage_Sqlite$ReventlessLocal.Make(TestBus)(DbProvider);
189
+ let s = Storage.make("agg", opts);
190
+ let ops = await TestRunner$ReventlessLocal.resolve(s.operations);
191
+ let none = await ops.latestSnapshot("id-snap");
192
+ globalThis.expect(none).toEqual({
193
+ TAG: "Ok",
194
+ _0: undefined
195
+ });
196
+ let state = Object.fromEntries([[
197
+ "names",
198
+ 2
199
+ ]]);
200
+ let w1 = await ops.writeSnapshot("id-snap", {
201
+ seqNr: 2,
202
+ state: state,
203
+ schemaHash: "h1"
204
+ });
205
+ globalThis.expect(w1).toEqual({
206
+ TAG: "Ok",
207
+ _0: undefined
208
+ });
209
+ let r1 = await ops.latestSnapshot("id-snap");
210
+ globalThis.expect(r1).toEqual({
211
+ TAG: "Ok",
212
+ _0: {
213
+ seqNr: 2,
214
+ state: state,
215
+ schemaHash: "h1"
216
+ }
217
+ });
218
+ let state5 = Object.fromEntries([[
219
+ "names",
220
+ 5
221
+ ]]);
222
+ await ops.writeSnapshot("id-snap", {
223
+ seqNr: 5,
224
+ state: state5,
225
+ schemaHash: "h2"
226
+ });
227
+ let r2 = await ops.latestSnapshot("id-snap");
228
+ globalThis.expect(r2).toEqual({
229
+ TAG: "Ok",
230
+ _0: {
231
+ seqNr: 5,
232
+ state: state5,
233
+ schemaHash: "h2"
234
+ }
235
+ });
236
+ });
237
+ globalThis.test("snapshots persist across a reopen of the database file", async () => {
238
+ let path = `/tmp/reventless-test-snapshot-` + Date.now().toString() + `.db`;
239
+ let state = Object.fromEntries([[
240
+ "v",
241
+ 7
242
+ ]]);
243
+ let TestBus = LocalBus$ReventlessLocal.Make({});
244
+ let db = SqliteDriver$ReventlessLocal.openDb(path);
245
+ let DbProvider = {
246
+ db: db
247
+ };
248
+ let Storage = EventLogStorage_Sqlite$ReventlessLocal.Make(TestBus)(DbProvider);
249
+ let s = Storage.make("persist", opts);
250
+ let ops = await TestRunner$ReventlessLocal.resolve(s.operations);
251
+ await ops.writeSnapshot("id-sp", {
252
+ seqNr: 3,
253
+ state: state,
254
+ schemaHash: "h"
255
+ });
256
+ SqliteDriver$ReventlessLocal.close(db);
257
+ let TestBus2 = LocalBus$ReventlessLocal.Make({});
258
+ let db$1 = SqliteDriver$ReventlessLocal.openDb(path);
259
+ let DbProvider2 = {
260
+ db: db$1
261
+ };
262
+ let Storage2 = EventLogStorage_Sqlite$ReventlessLocal.Make(TestBus2)(DbProvider2);
263
+ let s2 = Storage2.make("persist", opts);
264
+ let ops2 = await TestRunner$ReventlessLocal.resolve(s2.operations);
265
+ let r = await ops2.latestSnapshot("id-sp");
266
+ globalThis.expect(r).toEqual({
267
+ TAG: "Ok",
268
+ _0: {
269
+ seqNr: 3,
270
+ state: state,
271
+ schemaHash: "h"
272
+ }
273
+ });
274
+ return SqliteDriver$ReventlessLocal.close(db$1);
275
+ });
87
276
  globalThis.test("events persist across a reopen of the database file", async () => {
88
277
  let path = `/tmp/reventless-test-eventlog-` + Date.now().toString() + `.db`;
89
278
  let TestBus = LocalBus$ReventlessLocal.Make({});