@reventlessdev/reventless-local 3.0.0-alpha.249 → 3.0.0-alpha.251

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 (174) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/package.json +13 -13
  3. package/src/BakedManifest.res +5 -6
  4. package/src/EmitCapabilities.res +3 -1
  5. package/src/LocalPlatformRegistry.res +3 -1
  6. package/src/LocalPlatformStart.res +7 -6
  7. package/src/LocalSeedTarget.res +3 -1
  8. package/src/Platform.res +291 -215
  9. package/src/ShellConfig.res +10 -17
  10. package/src/UiHints.res +8 -14
  11. package/src/UiSlots.res +10 -17
  12. package/src/adapter/Api/LocalEvents_Server.res +5 -3
  13. package/src/adapter/Api/LocalGraphQL_Adapter.res +4 -4
  14. package/src/adapter/Auth/LocalAuth.res +12 -16
  15. package/src/adapter/Auth/LocalAuth.res.mjs +2 -1
  16. package/src/adapter/Auth/UserStore.res +13 -40
  17. package/src/adapter/Auth/UserStore.res.mjs +11 -67
  18. package/src/adapter/Backend.res +1 -2
  19. package/src/adapter/CommandGenerator/CommandGeneratorResolvers_GraphQL.res +42 -26
  20. package/src/adapter/CommandGenerator/InboundTranslationResolvers_GraphQL.res +13 -3
  21. package/src/adapter/CommandTopic/LocalCommandTopicChannel.res +15 -17
  22. package/src/adapter/CommandTopic/LocalCommandTopicRemoteChannel.res +18 -22
  23. package/src/adapter/Counter/LocalCounterHandler.res +1 -1
  24. package/src/adapter/DcbEventLog/DcbEventLogStorage_InMemory.res +33 -22
  25. package/src/adapter/DcbEventLog/DcbEventLogStorage_Sqlite.res +57 -57
  26. package/src/adapter/DomainGraphQL_Server.res +35 -34
  27. package/src/adapter/DomainMCP_Server.res +45 -41
  28. package/src/adapter/EventCollector/LocalEventCollectorChannel.res +47 -36
  29. package/src/adapter/EventHistory/EventHistoryResolvers_GraphQL.res +15 -12
  30. package/src/adapter/EventLog/EventLogStorage_InMemory.res +9 -4
  31. package/src/adapter/EventLog/EventLogStorage_Sqlite.res +36 -24
  32. package/src/adapter/EventTopic/LocalEventTopicPublisher.res +9 -3
  33. package/src/adapter/Heartbeat/LocalHeartbeatRunner.res +9 -14
  34. package/src/adapter/LocalBus.res +12 -12
  35. package/src/adapter/LocalCapabilities.res +6 -0
  36. package/src/adapter/LocalCapabilities.res.mjs +5 -2
  37. package/src/adapter/LocalGeocodeResolvers.res +1 -4
  38. package/src/adapter/LocalSideEffectHandler.res +1 -1
  39. package/src/adapter/LocalStateChangeDescriptor.res +2 -1
  40. package/src/adapter/MCP_ServerInstance.res +51 -39
  41. package/src/adapter/ObjectStore/LocalObjectStore.res +5 -2
  42. package/src/adapter/ObjectStore/ObjectStoreStorage_FileSystem.res +1 -2
  43. package/src/adapter/PlatformGraphQL_Server.res +10 -6
  44. package/src/adapter/PlatformGraphQL_Server.res.mjs +2 -1
  45. package/src/adapter/ProjectionCheckpoint.res +8 -1
  46. package/src/adapter/ProjectionPending.res +1 -3
  47. package/src/adapter/QueryDb/LocalQueryDbStorage.res +2 -1
  48. package/src/adapter/QueryDb/QueryDbResolvers_GraphQL.res +367 -269
  49. package/src/adapter/QueryDb/QueryDbStorage_InMemory.res +5 -3
  50. package/src/adapter/QueryDb/QueryDbStorage_Sqlite.res +63 -40
  51. package/src/adapter/QueryDb/SortKey_Filter.res +14 -6
  52. package/src/adapter/Runtime/LocalAggregateRuntime_Builder.res +12 -10
  53. package/src/adapter/Runtime/LocalEventCollectorRuntime_Builder.res +8 -2
  54. package/src/adapter/Runtime/LocalPluginRuntime_Builder.res +1 -4
  55. package/src/adapter/Scheduler/LocalScheduledPublisher.res +12 -12
  56. package/src/adapter/SqliteDriver.res +16 -4
  57. package/src/adapter/Task/TaskBucket_Sqlite.res +5 -6
  58. package/src/components/Aggregate_Builder.res +6 -1
  59. package/src/components/Counter_Builder.res +1 -5
  60. package/src/components/DcbEventLog_Builder.res +5 -3
  61. package/src/components/ExtensionPoint_Builder.res +13 -15
  62. package/src/components/InboundTranslationSlice_Builder.res +3 -2
  63. package/src/components/OutboundTranslationSlice_Builder.res +3 -2
  64. package/src/components/ReadModel_Builder.res +18 -20
  65. package/src/components/StateViewSlice_Builder.res +3 -2
  66. package/src/components/Task_Builder.res +12 -13
  67. package/src/reset/LocalSeedReset.res +2 -5
  68. package/src/test/Mocks/MockDcbEventLogStorage.res +5 -1
  69. package/src/test/Mocks/MockEventLogStorage.res +8 -3
  70. package/src/test/Mocks/MockQueryDbStorage.res +3 -2
  71. package/src/test/TestRunner.res +1 -1
  72. package/tests/BakedManifestFilesTest.res +7 -10
  73. package/tests/LocalEventTapTest.res +26 -21
  74. package/tests/LocalPlatformStartTest.res +33 -28
  75. package/tests/PluginEventDecodeTest.res +3 -3
  76. package/tests/ShellConfigTest.res +30 -21
  77. package/tests/SplitApiFixtures.res +34 -10
  78. package/tests/SplitApiTest.res +92 -62
  79. package/tests/adapter/BackendParityTest.res +180 -130
  80. package/tests/adapter/CollectNEventsTest.res +6 -4
  81. package/tests/adapter/CommandAuthorizationTest.res +47 -41
  82. package/tests/adapter/CommandTopicChannelTest.res +163 -139
  83. package/tests/adapter/CounterHandlerTest.res +95 -77
  84. package/tests/adapter/DcbEventLogStorageSqliteTest.res +171 -168
  85. package/tests/adapter/DcbEventLogStorageTest.res +264 -207
  86. package/tests/adapter/DomainGraphQL_ServerTest.res +53 -43
  87. package/tests/adapter/EventCollectorChannelTest.res +153 -124
  88. package/tests/adapter/EventHistoryResolverTest.res +46 -18
  89. package/tests/adapter/EventLogSnapshotParityTest.res +57 -45
  90. package/tests/adapter/EventLogStorageSqliteTest.res +9 -10
  91. package/tests/adapter/EventTapTest.res +37 -20
  92. package/tests/adapter/EventTopicPublisherTest.res +74 -50
  93. package/tests/adapter/GraphQLSubscriptionBridgeTest.res +4 -3
  94. package/tests/adapter/GraphQL_SchemaInspectorTest.res +666 -568
  95. package/tests/adapter/GraphQL_SubscriptionResolversTest.res +105 -96
  96. package/tests/adapter/HeartbeatRunnerTest.res +99 -81
  97. package/tests/adapter/InboundTranslationMutationTest.res +4 -1
  98. package/tests/adapter/LocalAuthHttpTest.res +79 -85
  99. package/tests/adapter/LocalAuthLoginTest.res +42 -50
  100. package/tests/adapter/LocalAuthTest.res +8 -16
  101. package/tests/adapter/LocalAuthUserStoreTest.res +2 -7
  102. package/tests/adapter/LocalBusBoundedTest.res +187 -117
  103. package/tests/adapter/LocalBusPubSubTest.res +118 -76
  104. package/tests/adapter/LocalDispatchContextTest.res +1 -2
  105. package/tests/adapter/LocalEvents_ServerTest.res +242 -187
  106. package/tests/adapter/PgProjectionCatchupTest.res +44 -10
  107. package/tests/adapter/ProjectionCheckpointTest.res +261 -206
  108. package/tests/adapter/QueryDbGsiTtlTest.res +123 -83
  109. package/tests/adapter/QueryDbListPushdownParityTest.res +107 -67
  110. package/tests/adapter/QueryDbListResolverTest.res +225 -208
  111. package/tests/adapter/QueryDbResolvedFieldsTest.res +17 -14
  112. package/tests/adapter/QueryDbStorageSqliteTest.res +33 -49
  113. package/tests/adapter/QueryDbStorageTest.res +163 -117
  114. package/tests/adapter/QueryDbUnionRoundTripTest.res +8 -10
  115. package/tests/adapter/QueryEngineTest.res +179 -126
  116. package/tests/adapter/ScheduledPublisherTest.res +171 -135
  117. package/tests/adapter/ServedBucketHttpTest.res +65 -59
  118. package/tests/adapter/SortKey_FilterTest.res +19 -3
  119. package/tests/adapter/SqliteDriverTest.res +13 -10
  120. package/tests/adapter/TaskBucketTest.res +94 -80
  121. package/tests/components/aggregate/AggregateTest.res +31 -18
  122. package/tests/components/automationslice/AutomationSliceCallbackTest.res +259 -222
  123. package/tests/components/automationslice/AutomationSliceFixtures.res +4 -2
  124. package/tests/components/automationslice/AutomationSliceSelfDeadlockFixtures.res +38 -38
  125. package/tests/components/automationslice/MixedSourceAutomationSliceFixtures.res +12 -14
  126. package/tests/components/automationslice/MixedSourceAutomationSliceTest.res +8 -32
  127. package/tests/components/commandgenerator/CommandGeneratorFixtures.res +7 -5
  128. package/tests/components/commandgenerator/CommandGeneratorTest.res +40 -31
  129. package/tests/components/commandtopic/CommandTopicFixtures.res +3 -4
  130. package/tests/components/commandtopic/CommandTopicStreamFixtures.res +7 -5
  131. package/tests/components/commandtopic/CommandTopicTest.res +24 -15
  132. package/tests/components/counter/CounterFixtures.res +6 -8
  133. package/tests/components/dcb/DcbCrossPartitionFixtures.res +8 -2
  134. package/tests/components/dcb/DcbCrossPartitionReadTest.res +20 -13
  135. package/tests/components/dcb/DcbEventLogAppendStreamTest.res +12 -8
  136. package/tests/components/dcb/DcbEventLogStreamTest.res +88 -61
  137. package/tests/components/dcb/DcbFixtures.res +4 -1
  138. package/tests/components/dcb/DcbTest.res +9 -6
  139. package/tests/components/eventcollector/EventCollectorFixtures.res +0 -1
  140. package/tests/components/eventcollector/EventCollectorTest.res +6 -3
  141. package/tests/components/eventlog/EventLogAppendStreamTest.res +88 -68
  142. package/tests/components/eventlog/EventLogConflictTest.res +8 -4
  143. package/tests/components/eventlog/EventLogFixtures.res +3 -3
  144. package/tests/components/eventlog/EventLogProvisioningSeamTest.res +26 -13
  145. package/tests/components/eventlog/EventLogStreamTest.res +119 -56
  146. package/tests/components/eventlog/EventLogTest.res +20 -4
  147. package/tests/components/eventtopic/EventTopicFixtures.res +3 -3
  148. package/tests/components/eventtopic/EventTopicStreamFixtures.res +1 -2
  149. package/tests/components/eventtopic/EventTopicStreamTest.res +7 -1
  150. package/tests/components/extensionpoint/ExtensionPointFixtures.res +14 -9
  151. package/tests/components/extensionpoint/ExtensionPointTest.res +3 -2
  152. package/tests/components/heartbeat/HeartbeatFixtures.res +5 -6
  153. package/tests/components/heartbeat/HeartbeatTest.res +56 -47
  154. package/tests/components/inboundtranslationslice/InboundTranslationSliceCallbackTest.res +271 -249
  155. package/tests/components/inboundtranslationslice/InboundTranslationSliceFixtures.res +3 -2
  156. package/tests/components/outboundtranslationslice/OutboundTranslationSliceCallbackTest.res +361 -276
  157. package/tests/components/outboundtranslationslice/OutboundTranslationSliceFixtures.res +8 -10
  158. package/tests/components/outboundtranslationslice/OutboundTranslationSlicePlatformFixtures.res +37 -43
  159. package/tests/components/querydb/QueryDbFixtures.res +1 -5
  160. package/tests/components/querydb/QueryDbTest.res +2 -4
  161. package/tests/components/readmodel/DcbReadModelE2EFixtures.res +15 -18
  162. package/tests/components/readmodel/MixedSourceReadModelFixtures.res +5 -7
  163. package/tests/components/readmodel/ReadModelFixtures.res +8 -8
  164. package/tests/components/scheduler/SchedulerTest.res +76 -58
  165. package/tests/components/sideeffecthandler/SideEffectHandlerFixtures.res +4 -4
  166. package/tests/components/stateviewslice/StateViewSliceDisplayNameFixtures.res +5 -7
  167. package/tests/components/stateviewslice/StateViewSliceFixtures.res +8 -6
  168. package/tests/components/stateviewslice/StateViewSliceSubIdFixtures.res +17 -11
  169. package/tests/components/stateviewslice/StateViewSliceSubIdTest.res +12 -2
  170. package/tests/components/task/TaskFixtures.res +5 -5
  171. package/tests/components/task/TaskTest.res +95 -80
  172. package/tests/plugin/PluginsProjection_GWT.res +3 -1
  173. package/tests/plugin/UiFragments_GWT.res +6 -1
  174. package/tests/reset/LocalSeedResetTest.res +1 -0
@@ -14,90 +14,117 @@ describe("DcbEventLog.readStream (in-memory adapter)", () => {
14
14
  })
15
15
 
16
16
  describe("basic streaming", () => {
17
- testPromise("readStream for tag with no matching events returns empty stream", async () => {
18
- let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
19
- let count = await ops.readStream(~query=tagQuery("no-such-id"))
17
+ testPromise(
18
+ "readStream for tag with no matching events returns empty stream",
19
+ async () => {
20
+ let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
21
+ let count = await ops.readStream(~query=tagQuery("no-such-id"))
20
22
  ->Stream.runFold(0, (n, _) => n + 1)
21
23
  ->Effect.runPromise
22
- expect(count)->toBe(0)
23
- })
24
+ expect(count)->toBe(0)
25
+ },
26
+ )
24
27
 
25
- testPromise("readStream emits all events matching a tag query", async () => {
26
- let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
27
- let _ = await dispatch(addItemJson("stream-1", "Widget"), "stream-1")
28
- let count = await ops.readStream(~query=tagQuery("stream-1"))
28
+ testPromise(
29
+ "readStream emits all events matching a tag query",
30
+ async () => {
31
+ let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
32
+ let _ = await dispatch(addItemJson("stream-1", "Widget"), "stream-1")
33
+ let count = await ops.readStream(~query=tagQuery("stream-1"))
29
34
  ->Stream.runFold(0, (n, _) => n + 1)
30
35
  ->Effect.runPromise
31
- expect(count)->toBe(1)
32
- })
36
+ expect(count)->toBe(1)
37
+ },
38
+ )
33
39
 
34
- testPromise("readStream for distinct IDs returns independent streams", async () => {
35
- let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
36
- let _ = await dispatch(addItemJson("stream-A", "Alpha"), "stream-A")
37
- let _ = await dispatch(addItemJson("stream-B", "Beta"), "stream-B")
38
- let countA = await ops.readStream(~query=tagQuery("stream-A"))
40
+ testPromise(
41
+ "readStream for distinct IDs returns independent streams",
42
+ async () => {
43
+ let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
44
+ let _ = await dispatch(addItemJson("stream-A", "Alpha"), "stream-A")
45
+ let _ = await dispatch(addItemJson("stream-B", "Beta"), "stream-B")
46
+ let countA = await ops.readStream(~query=tagQuery("stream-A"))
39
47
  ->Stream.runFold(0, (n, _) => n + 1)
40
48
  ->Effect.runPromise
41
- let countB = await ops.readStream(~query=tagQuery("stream-B"))
49
+ let countB = await ops.readStream(~query=tagQuery("stream-B"))
42
50
  ->Stream.runFold(0, (n, _) => n + 1)
43
51
  ->Effect.runPromise
44
- expect(countA)->toBe(1)
45
- expect(countB)->toBe(1)
46
- })
52
+ expect(countA)->toBe(1)
53
+ expect(countB)->toBe(1)
54
+ },
55
+ )
47
56
 
48
- testPromise("take(1) limits stream without loading all", async () => {
49
- let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
50
- let _ = await dispatch(addItemJson("stream-C1", "C1"), "stream-C1")
51
- let _ = await dispatch(addItemJson("stream-C2", "C2"), "stream-C2")
52
- // Broad query: all ItemAdded events regardless of tag value
53
- let result = await ops.readStream(~query=typeQuery("ItemAdded"))
57
+ testPromise(
58
+ "take(1) limits stream without loading all",
59
+ async () => {
60
+ let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
61
+ let _ = await dispatch(addItemJson("stream-C1", "C1"), "stream-C1")
62
+ let _ = await dispatch(addItemJson("stream-C2", "C2"), "stream-C2")
63
+ // Broad query: all ItemAdded events regardless of tag value
64
+ let result = await ops.readStream(~query=typeQuery("ItemAdded"))
54
65
  ->Stream.take(1)
55
66
  ->Stream.runCollect
56
67
  ->Effect.runPromise
57
- expect(result->Array.length)->toBe(1)
58
- })
68
+ expect(result->Array.length)->toBe(1)
69
+ },
70
+ )
59
71
  })
60
72
 
61
73
  describe("fold patterns — (state, headPosition) used in StateChangeSlice_Callback", () => {
62
- testPromise("runFold extracts last headPosition for append condition", async () => {
63
- let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
64
- let _ = await dispatch(addItemJson("fold-1", "FoldItem"), "fold-1")
65
- let (_, headPosition) = await ops.readStream(~query=tagQuery("fold-1"))
74
+ testPromise(
75
+ "runFold extracts last headPosition for append condition",
76
+ async () => {
77
+ let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
78
+ let _ = await dispatch(addItemJson("fold-1", "FoldItem"), "fold-1")
79
+ let (_, headPosition) = await ops.readStream(~query=tagQuery("fold-1"))
66
80
  ->Stream.runFold(((), None), ((_dm, _pos), se) => ((), Some(se.position)))
67
81
  ->Effect.runPromise
68
- expect(Option.isSome(headPosition))->toBe(true)
69
- })
82
+ expect(Option.isSome(headPosition))->toBe(true)
83
+ },
84
+ )
70
85
 
71
- testPromise("runFold on empty stream returns initial accumulator", async () => {
72
- let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
73
- let (_, headPosition) = await ops.readStream(~query=tagQuery("no-such-id-fold"))
86
+ testPromise(
87
+ "runFold on empty stream returns initial accumulator",
88
+ async () => {
89
+ let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
90
+ let (_, headPosition) = await ops.readStream(~query=tagQuery("no-such-id-fold"))
74
91
  ->Stream.runFold((false, None), ((_dm, _pos), se) => (true, Some(se.position)))
75
92
  ->Effect.runPromise
76
- expect(headPosition)->toEqual(None)
77
- })
93
+ expect(headPosition)->toEqual(None)
94
+ },
95
+ )
78
96
  })
79
97
 
80
- describe("StateChangeSlice regression — second command sees first event via stream fold", () => {
81
- testPromise("second AddItem with same ID is rejected (state sees existing event)", async () => {
82
- // First command succeeds → ItemAdded event stored
83
- // Second command: readStream folds all events → state = true (exists)
84
- // decide(true, AddItem) Error(ItemAlreadyExists) no new events
85
- let id = "dup-test-stream"
86
- let _ = await dispatch(addItemJson(id, "First"), id)
87
- let countBefore = capturedEventCount.contents
88
- let _ = await dispatch(addItemJson(id, "Duplicate"), id)
89
- // Duplicate command must not publish a second event
90
- expect(capturedEventCount.contents)->toBe(countBefore)
91
- })
98
+ describe(
99
+ "StateChangeSlice regression second command sees first event via stream fold",
100
+ () => {
101
+ testPromise(
102
+ "second AddItem with same ID is rejected (state sees existing event)",
103
+ async () => {
104
+ // First command succeeds ItemAdded event stored
105
+ // Second command: readStream folds all events → state = true (exists)
106
+ // decide(true, AddItem) Error(ItemAlreadyExists) → no new events
107
+ let id = "dup-test-stream"
108
+ let _ = await dispatch(addItemJson(id, "First"), id)
109
+ let countBefore = capturedEventCount.contents
110
+ let _ = await dispatch(addItemJson(id, "Duplicate"), id)
111
+ // Duplicate command must not publish a second event
112
+ expect(capturedEventCount.contents)->toBe(countBefore)
113
+ },
114
+ )
92
115
 
93
- testPromise("second AddItem with different ID succeeds", async () => {
94
- let id1 = "distinct-1"
95
- let id2 = "distinct-2"
96
- let _ = await dispatch(addItemJson(id1, "First"), id1)
97
- let countAfterFirst = capturedEventCount.contents
98
- let _ = await dispatch(addItemJson(id2, "Second"), id2)
99
- // New ID should publish an event
100
- expect(capturedEventCount.contents)->toBe(countAfterFirst + 1)
101
- })
102
- })
116
+ testPromise(
117
+ "second AddItem with different ID succeeds",
118
+ async () => {
119
+ let id1 = "distinct-1"
120
+ let id2 = "distinct-2"
121
+ let _ = await dispatch(addItemJson(id1, "First"), id1)
122
+ let countAfterFirst = capturedEventCount.contents
123
+ let _ = await dispatch(addItemJson(id2, "Second"), id2)
124
+ // New ID should publish an event
125
+ expect(capturedEventCount.contents)->toBe(countAfterFirst + 1)
126
+ },
127
+ )
128
+ },
129
+ )
103
130
  })
@@ -78,7 +78,10 @@ let _ = TestRunner.setup()
78
78
  // ─────────────────────────────────────────────────────────────
79
79
 
80
80
  module ItemEventLogMaker = DcbEventLog_Builder.Make(Bus)
81
- let eventLog = ItemEventLogMaker.make(~name="ItemEventLog", ~partitionTag=Reventless.DcbTag.Simple({key: "id"}))
81
+ let eventLog = ItemEventLogMaker.make(
82
+ ~name="ItemEventLog",
83
+ ~partitionTag=Reventless.DcbTag.Simple({key: "id"}),
84
+ )
82
85
 
83
86
  // ─────────────────────────────────────────────────────────────
84
87
  // Build StateChangeSlice
@@ -17,8 +17,9 @@ describe("DcbEventLog E2E", () => {
17
17
 
18
18
  testPromise("AddItem command publishes 1 event to event topic", async () => {
19
19
  let cmd =
20
- AddItemSpec.AddItem({id: "item-1", name: "Widget"})
21
- ->ReventlessCore.Message.encode(AddItemSpec.commandSchema)
20
+ AddItemSpec.AddItem({id: "item-1", name: "Widget"})->ReventlessCore.Message.encode(
21
+ AddItemSpec.commandSchema,
22
+ )
22
23
  await dispatch(cmd, "item-1")
23
24
  expect(capturedEventCount.contents)->toBe(1)
24
25
  })
@@ -26,16 +27,18 @@ describe("DcbEventLog E2E", () => {
26
27
  testPromise("duplicate AddItem produces 0 events (ItemAlreadyExists)", async () => {
27
28
  // item-1 was already created in the previous test — same aggregate state persists
28
29
  let cmd =
29
- AddItemSpec.AddItem({id: "item-1", name: "Widget"})
30
- ->ReventlessCore.Message.encode(AddItemSpec.commandSchema)
30
+ AddItemSpec.AddItem({id: "item-1", name: "Widget"})->ReventlessCore.Message.encode(
31
+ AddItemSpec.commandSchema,
32
+ )
31
33
  await dispatch(cmd, "item-1")
32
34
  expect(capturedEventCount.contents)->toBe(0)
33
35
  })
34
36
 
35
37
  testPromise("AddItem for new id produces 1 event", async () => {
36
38
  let cmd =
37
- AddItemSpec.AddItem({id: "item-2", name: "Gadget"})
38
- ->ReventlessCore.Message.encode(AddItemSpec.commandSchema)
39
+ AddItemSpec.AddItem({id: "item-2", name: "Gadget"})->ReventlessCore.Message.encode(
40
+ AddItemSpec.commandSchema,
41
+ )
39
42
  await dispatch(cmd, "item-2")
40
43
  expect(capturedEventCount.contents)->toBe(1)
41
44
  })
@@ -39,4 +39,3 @@ module EventCollectorMaker = ReventlessCore.EventCollector_Builder.Make(
39
39
  )
40
40
 
41
41
  let eventCollector = EventCollectorMaker.make(~name="TestEC", ~eventTopics=allEventTopics, ~opts={})
42
-
@@ -27,9 +27,12 @@ describe("EventCollector (in-memory)", () => {
27
27
  // The EventCollector itself doesn't expose a direct handler in our test setup —
28
28
  // verify the bus subscription mechanism works
29
29
  let received: ref<int> = ref(0)
30
- Bus.subscribeToEvents(topicName, async (_, _, _) => {
31
- received := received.contents + 1
32
- })
30
+ Bus.subscribeToEvents(
31
+ topicName,
32
+ async (_, _, _) => {
33
+ received := received.contents + 1
34
+ },
35
+ )
33
36
  let testEvent = JSON.Encode.object(Dict.fromArray([("type", JSON.Encode.string("TestEvent"))]))
34
37
  await Bus.publishEvent(topicName, "test", testMeta, testEvent)
35
38
  expect(received.contents)->toBe(1)
@@ -14,80 +14,100 @@ describe("EventLog.appendStream (in-memory adapter)", () => {
14
14
  let _ = beforeEach(() => reset())
15
15
 
16
16
  describe("basic append", () => {
17
- testPromise("appendStream writes all events to storage", async () => {
18
- let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
19
- let stream = [
20
- ItemEventLogSpec.ItemCreated({name: "e1"}),
21
- ItemEventLogSpec.ItemCreated({name: "e2"}),
22
- ItemEventLogSpec.ItemCreated({name: "e3"}),
23
- ]->Stream.fromIterable
24
- let _ = await ops.appendStream(0, "as-basic-1", stream)->Effect.runPromise
25
- let replayed = await ops.replay("as-basic-1")
26
- expect(replayed->Array.length)->toBe(3)
27
- })
17
+ testPromise(
18
+ "appendStream writes all events to storage",
19
+ async () => {
20
+ let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
21
+ let stream =
22
+ [
23
+ ItemEventLogSpec.ItemCreated({name: "e1"}),
24
+ ItemEventLogSpec.ItemCreated({name: "e2"}),
25
+ ItemEventLogSpec.ItemCreated({name: "e3"}),
26
+ ]->Stream.fromIterable
27
+ let _ = await ops.appendStream(0, "as-basic-1", stream)->Effect.runPromise
28
+ let replayed = await ops.replay("as-basic-1")
29
+ expect(replayed->Array.length)->toBe(3)
30
+ },
31
+ )
28
32
 
29
- testPromise("appendStream on empty stream writes nothing", async () => {
30
- let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
31
- let _ = await ops.appendStream(0, "as-empty-1", Stream.empty)->Effect.runPromise
32
- let replayed = await ops.replay("as-empty-1")
33
- expect(replayed->Array.length)->toBe(0)
34
- })
33
+ testPromise(
34
+ "appendStream on empty stream writes nothing",
35
+ async () => {
36
+ let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
37
+ let _ = await ops.appendStream(0, "as-empty-1", Stream.empty)->Effect.runPromise
38
+ let replayed = await ops.replay("as-empty-1")
39
+ expect(replayed->Array.length)->toBe(0)
40
+ },
41
+ )
35
42
 
36
- testPromise("appendStream preserves event order", async () => {
37
- let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
38
- let stream = [
39
- ItemEventLogSpec.ItemCreated({name: "first"}),
40
- ItemEventLogSpec.ItemDeleted({id: "second"}),
41
- ]->Stream.fromIterable
42
- let _ = await ops.appendStream(0, "as-order-1", stream)->Effect.runPromise
43
- let replayed = await ops.replayStream("as-order-1")->Stream.runCollect->Effect.runPromise
44
- let first = replayed->Array.getUnsafe(0)
45
- expect(first)->toEqual(ItemEventLogSpec.ItemCreated({name: "first"}))
46
- })
43
+ testPromise(
44
+ "appendStream preserves event order",
45
+ async () => {
46
+ let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
47
+ let stream =
48
+ [
49
+ ItemEventLogSpec.ItemCreated({name: "first"}),
50
+ ItemEventLogSpec.ItemDeleted({id: "second"}),
51
+ ]->Stream.fromIterable
52
+ let _ = await ops.appendStream(0, "as-order-1", stream)->Effect.runPromise
53
+ let replayed = await ops.replayStream("as-order-1")->Stream.runCollect->Effect.runPromise
54
+ let first = replayed->Array.getUnsafe(0)
55
+ expect(first)->toEqual(ItemEventLogSpec.ItemCreated({name: "first"}))
56
+ },
57
+ )
47
58
 
48
- testPromise("appendStream after array append continues from correct sequenceNr", async () => {
49
- let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
50
- let _ = await ops.append(
51
- 0,
52
- "as-seq-1",
53
- [makeEvent'("as-seq-1", ItemEventLogSpec.ItemCreated({name: "initial"}))],
54
- )
55
- let stream = [ItemEventLogSpec.ItemCreated({name: "streamed"})]->Stream.fromIterable
56
- let _ = await ops.appendStream(1, "as-seq-1", stream)->Effect.runPromise
57
- let replayed = await ops.replay("as-seq-1")
58
- expect(replayed->Array.length)->toBe(2)
59
- })
59
+ testPromise(
60
+ "appendStream after array append continues from correct sequenceNr",
61
+ async () => {
62
+ let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
63
+ let _ = await ops.append(
64
+ 0,
65
+ "as-seq-1",
66
+ [makeEvent'("as-seq-1", ItemEventLogSpec.ItemCreated({name: "initial"}))],
67
+ )
68
+ let stream = [ItemEventLogSpec.ItemCreated({name: "streamed"})]->Stream.fromIterable
69
+ let _ = await ops.appendStream(1, "as-seq-1", stream)->Effect.runPromise
70
+ let replayed = await ops.replay("as-seq-1")
71
+ expect(replayed->Array.length)->toBe(2)
72
+ },
73
+ )
60
74
  })
61
75
 
62
76
  describe("replayStream → appendStream pipeline", () => {
63
- testPromise("can copy events from one aggregate to another via streams", async () => {
64
- let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
65
- let _ = await ops.append(
66
- 0,
67
- "as-src-1",
68
- [
69
- makeEvent'("as-src-1", ItemEventLogSpec.ItemCreated({name: "a"})),
70
- makeEvent'("as-src-1", ItemEventLogSpec.ItemDeleted({id: "b"})),
71
- ],
72
- )
73
- let sourceStream = ops.replayStream("as-src-1")
74
- let _ = await ops.appendStream(0, "as-dst-1", sourceStream)->Effect.runPromise
75
- let destEvents = await ops.replay("as-dst-1")
76
- expect(destEvents->Array.length)->toBe(2)
77
- })
77
+ testPromise(
78
+ "can copy events from one aggregate to another via streams",
79
+ async () => {
80
+ let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
81
+ let _ = await ops.append(
82
+ 0,
83
+ "as-src-1",
84
+ [
85
+ makeEvent'("as-src-1", ItemEventLogSpec.ItemCreated({name: "a"})),
86
+ makeEvent'("as-src-1", ItemEventLogSpec.ItemDeleted({id: "b"})),
87
+ ],
88
+ )
89
+ let sourceStream = ops.replayStream("as-src-1")
90
+ let _ = await ops.appendStream(0, "as-dst-1", sourceStream)->Effect.runPromise
91
+ let destEvents = await ops.replay("as-dst-1")
92
+ expect(destEvents->Array.length)->toBe(2)
93
+ },
94
+ )
78
95
 
79
- testPromise("copied events round-trip through decode correctly", async () => {
80
- let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
81
- let _ = await ops.append(
82
- 0,
83
- "as-src-2",
84
- [makeEvent'("as-src-2", ItemEventLogSpec.ItemCreated({name: "copied"}))],
85
- )
86
- let sourceStream = ops.replayStream("as-src-2")
87
- let _ = await ops.appendStream(0, "as-dst-2", sourceStream)->Effect.runPromise
88
- let destEvents = await ops.replayStream("as-dst-2")->Stream.runCollect->Effect.runPromise
89
- let first = destEvents->Array.getUnsafe(0)
90
- expect(first)->toEqual(ItemEventLogSpec.ItemCreated({name: "copied"}))
91
- })
96
+ testPromise(
97
+ "copied events round-trip through decode correctly",
98
+ async () => {
99
+ let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
100
+ let _ = await ops.append(
101
+ 0,
102
+ "as-src-2",
103
+ [makeEvent'("as-src-2", ItemEventLogSpec.ItemCreated({name: "copied"}))],
104
+ )
105
+ let sourceStream = ops.replayStream("as-src-2")
106
+ let _ = await ops.appendStream(0, "as-dst-2", sourceStream)->Effect.runPromise
107
+ let destEvents = await ops.replayStream("as-dst-2")->Stream.runCollect->Effect.runPromise
108
+ let first = destEvents->Array.getUnsafe(0)
109
+ expect(first)->toEqual(ItemEventLogSpec.ItemCreated({name: "copied"}))
110
+ },
111
+ )
92
112
  })
93
113
  })
@@ -75,10 +75,14 @@ describe("EventLog — conflict detection (in-memory)", () => {
75
75
 
76
76
  testPromise("batch append with correct sequenceNr stores all events", async () => {
77
77
  let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
78
- let result = await ops.append(0, "conflict-5", [
79
- makeEvent'("conflict-5", ItemEventLogSpec.ItemCreated({name: "e1"})),
80
- makeEvent'("conflict-5", ItemEventLogSpec.ItemDeleted({id: "e2"})),
81
- ])
78
+ let result = await ops.append(
79
+ 0,
80
+ "conflict-5",
81
+ [
82
+ makeEvent'("conflict-5", ItemEventLogSpec.ItemCreated({name: "e1"})),
83
+ makeEvent'("conflict-5", ItemEventLogSpec.ItemDeleted({id: "e2"})),
84
+ ],
85
+ )
82
86
  expect(Result.isOk(result))->toBe(true)
83
87
  let replayed = await ops.replay("conflict-5")
84
88
  expect(replayed->Array.length)->toBe(2)
@@ -11,7 +11,7 @@ module ItemEventLogSpec = {
11
11
  let name = "TestItemEventLog"
12
12
 
13
13
  @schema
14
- type event = | ItemCreated({name: string}) | ItemDeleted({id: string})
14
+ type event = ItemCreated({name: string}) | ItemDeleted({id: string})
15
15
  }
16
16
 
17
17
  // ─────────────────────────────────────────────────────────────
@@ -41,11 +41,11 @@ module EventLogMaker = ReventlessCore.EventLog_Builder.Make(
41
41
 
42
42
  let eventLog = EventLogMaker.make(~name="TestItemEventLog")
43
43
 
44
- let makeEvent' = (id, event) => ({
44
+ let makeEvent' = (id, event): Reventless.Message.event'<string, ItemEventLogSpec.event> => {
45
45
  Reventless.Message.id,
46
46
  meta: testMeta,
47
47
  event,
48
- }: Reventless.Message.event'<string, ItemEventLogSpec.event>)
48
+ }
49
49
 
50
50
  let reset = () => {
51
51
  capturedTopicEventCount := 0
@@ -30,7 +30,9 @@ module Recorder: ReventlessCore.EventLogProvisioning.Backend = {
30
30
  })
31
31
  }
32
32
 
33
- ReventlessCore.EventLogProvisioning.use(module(Recorder: ReventlessCore.EventLogProvisioning.Backend))
33
+ ReventlessCore.EventLogProvisioning.use(
34
+ module(Recorder: ReventlessCore.EventLogProvisioning.Backend),
35
+ )
34
36
 
35
37
  module Bus = LocalBus.Make()
36
38
 
@@ -42,7 +44,7 @@ module SeamItemSpec = {
42
44
  let name = "SeamItemEventLog"
43
45
 
44
46
  @schema
45
- type event = | SeamItemCreated({name: string})
47
+ type event = SeamItemCreated({name: string})
46
48
  }
47
49
 
48
50
  module ClassicMaker = ReventlessCore.EventLog_Builder.Make(
@@ -53,7 +55,10 @@ module ClassicMaker = ReventlessCore.EventLog_Builder.Make(
53
55
 
54
56
  // The plugin builder publishes this context around a plugin's construct; entering
55
57
  // it here is what a real plugin build does, so the seam should carry the names.
56
- let previousContext = ReventlessCore.ResourceAttribution.enter(~platform="seam-platform", ~plugin="SeamPlugin")
58
+ let previousContext = ReventlessCore.ResourceAttribution.enter(
59
+ ~platform="seam-platform",
60
+ ~plugin="SeamPlugin",
61
+ )
57
62
 
58
63
  let classicLog = ClassicMaker.make(
59
64
  ~name="SeamItem",
@@ -63,10 +68,7 @@ let classicLog = ClassicMaker.make(
63
68
  // ── DCB log ──────────────────────────────────────────────────
64
69
  module DcbMaker = DcbEventLog_Builder.Make(Bus)
65
70
 
66
- let dcbLog = DcbMaker.make(
67
- ~name="SeamCatalog",
68
- ~partitionTag=Reventless.DcbTag.Simple({key: "id"}),
69
- )
71
+ let dcbLog = DcbMaker.make(~name="SeamCatalog", ~partitionTag=Reventless.DcbTag.Simple({key: "id"}))
70
72
 
71
73
  ReventlessCore.ResourceAttribution.restore(previousContext)
72
74
 
@@ -80,16 +82,27 @@ describe("EventLogProvisioning seam, driven by the real builders", () => {
80
82
 
81
83
  testSync("carries the owning element: the aggregate for classic, the plugin for DCB", () => {
82
84
  expect(provisioned->Array.map(r => r.owner))->toEqual([
83
- Some(({kind: ReventlessCore.ComponentType.Aggregate, name: "SeamItem"}: ReventlessCore.ResourceAttribution.owner)),
84
- Some(({kind: ReventlessCore.ComponentType.Plugin, name: "SeamCatalog"}: ReventlessCore.ResourceAttribution.owner)),
85
+ Some(
86
+ (
87
+ {
88
+ kind: ReventlessCore.ComponentType.Aggregate,
89
+ name: "SeamItem",
90
+ }: ReventlessCore.ResourceAttribution.owner
91
+ ),
92
+ ),
93
+ Some(
94
+ (
95
+ {
96
+ kind: ReventlessCore.ComponentType.Plugin,
97
+ name: "SeamCatalog",
98
+ }: ReventlessCore.ResourceAttribution.owner
99
+ ),
100
+ ),
85
101
  ])
86
102
  })
87
103
 
88
104
  testSync("carries the ambient plugin attribution the builder published", () => {
89
- expect(provisioned->Array.map(r => r.plugin))->toEqual([
90
- Some("SeamPlugin"),
91
- Some("SeamPlugin"),
92
- ])
105
+ expect(provisioned->Array.map(r => r.plugin))->toEqual([Some("SeamPlugin"), Some("SeamPlugin")])
93
106
  })
94
107
 
95
108
  testSync("carries the adapter's resources — empty for in-memory storage", () => {