@reventlessdev/reventless-aws 3.0.0-alpha.344 → 3.0.0-alpha.345

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 (232) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/package.json +11 -11
  3. package/scripts/ProvisionIdentity.res +8 -3
  4. package/src/Platform.res +327 -328
  5. package/src/Platform.res.mjs +4 -2
  6. package/src/Platform_Casts.res +13 -4
  7. package/src/adapter/Api/AppSyncEventsSigner_Ops.res +8 -4
  8. package/src/adapter/Api/AppSync_EventsApi.res +12 -14
  9. package/src/adapter/Api/AppSync_Resolver_Native.res +3 -4
  10. package/src/adapter/Api/AppSync_Resolver_Retrying.res +78 -70
  11. package/src/adapter/Api/AppSync_SourceApiAssociation_Retrying.res +11 -8
  12. package/src/adapter/Api/Platform_AdminScan_Ops.res +15 -14
  13. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res +10 -2
  14. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res +40 -50
  15. package/src/adapter/Api/Platform_UIFragments_Lambda.res +39 -32
  16. package/src/adapter/Api/Platform_UIFragments_Lambda_Ops.res +5 -2
  17. package/src/adapter/Auth/Auth_ActiveRolePoolAttachment.res +0 -1
  18. package/src/adapter/Auth/Auth_ActiveRoleStore.res +1 -2
  19. package/src/adapter/Auth/Auth_ActiveRoleStore_Schema.res +1 -4
  20. package/src/adapter/Auth/Auth_ActiveRoleTrigger_Ops.res +5 -2
  21. package/src/adapter/Auth/Auth_Cognito.res +3 -9
  22. package/src/adapter/CommandGenerator/CommandGeneratorResolvers_AppSync.res +1 -4
  23. package/src/adapter/CommandGenerator/CommandGeneratorResolvers_AppSync_NoOp.res +3 -1
  24. package/src/adapter/CommandGenerator/InboundTranslationResolvers_AppSync.res +1 -4
  25. package/src/adapter/CommandTopic/CommandTopicChannel_SQS.res +7 -4
  26. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Async.res +6 -1
  27. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_FIFO.res +7 -4
  28. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Runtime.res +24 -18
  29. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Sync.res +9 -4
  30. package/src/adapter/CommandTopic/CommandTopicRegistry.res +1 -4
  31. package/src/adapter/Counter/CounterHandler_DynamoDbStream.res +15 -15
  32. package/src/adapter/DcbEventLog/DcbEventLogStorage_DynamoDb.res +9 -5
  33. package/src/adapter/DcbEventLog/DcbEventLogStorage_DynamoDb_Runtime.res +179 -213
  34. package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream.res +5 -5
  35. package/src/adapter/EventCollector/EventCollectorChannel_Helpers.res +6 -2
  36. package/src/adapter/EventCollector/EventCollectorChannel_SQS.res +14 -7
  37. package/src/adapter/EventCollector/EventCollectorChannel_SQS_FIFO.res +14 -7
  38. package/src/adapter/EventCollector/EventCollectorChannel_SQS_Runtime.res +6 -3
  39. package/src/adapter/EventLog/EventLogStorage_DynamoDbStream.res +6 -1
  40. package/src/adapter/EventLog/EventLogStorage_DynamoDb_Runtime.res +13 -17
  41. package/src/adapter/EventLog/EventLogStorage_Postgres.res +7 -4
  42. package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res +12 -7
  43. package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync_Ops.res +4 -2
  44. package/src/adapter/EventTopic/EventTopicPublisher_DynamoDbStream.res +3 -2
  45. package/src/adapter/EventTopic/EventTopicPublisher_SNS.res +20 -12
  46. package/src/adapter/EventTopic/EventTopicPublisher_SNS_FIFO.res +21 -9
  47. package/src/adapter/EventTopic/EventTopicPublisher_SNS_Runtime.res +5 -3
  48. package/src/adapter/Geocoder/Geocoder_AwsLocation_Resolver.res +6 -3
  49. package/src/adapter/Mcp/MCP_Lambda.res +31 -46
  50. package/src/adapter/Messaging/Messaging_Ses_Backend.res +11 -7
  51. package/src/adapter/Messaging/Messaging_Ses_Backend.res.mjs +2 -4
  52. package/src/adapter/Monitoring/Monitoring_CloudWatch.res +16 -16
  53. package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res +5 -10
  54. package/src/adapter/Postgres/PgChangeFeedRelay_Runtime.res +4 -2
  55. package/src/adapter/Postgres/PgConnection.res +6 -5
  56. package/src/adapter/Postgres/PgMigration_Builder.res +4 -8
  57. package/src/adapter/Postgres/PgRuntime.res +4 -7
  58. package/src/adapter/QueryDb/PgQueryResolver_Builder.res +9 -11
  59. package/src/adapter/QueryDb/PgQueryResolver_Lambda.res +22 -21
  60. package/src/adapter/QueryDb/QueryDbResolvers_AppSync.res +114 -111
  61. package/src/adapter/QueryDb/QueryDbResolvers_Lambda.res +1 -7
  62. package/src/adapter/QueryDb/QueryDbStorage.res +4 -2
  63. package/src/adapter/QueryDb/QueryDbStorage_DynamoDb.res +23 -24
  64. package/src/adapter/QueryDb/QueryDbStorage_DynamoDbStream.res +8 -2
  65. package/src/adapter/QueryDb/QueryDbStorage_DynamoDb_Runtime.res +71 -73
  66. package/src/adapter/QueryDb/QueryDbStorage_Postgres.res +5 -2
  67. package/src/adapter/QueryDb/QueryInterceptor_Provisioning.res +1 -4
  68. package/src/adapter/QueryEngine/QueryEngine_DynamoDb.res +8 -2
  69. package/src/adapter/Runtime/AggregateRuntime_Builder_Micro.res +73 -81
  70. package/src/adapter/Runtime/AggregateRuntime_Builder_Micro_Async.res +72 -78
  71. package/src/adapter/Runtime/AggregateRuntime_Builder_PerAggregate.res +44 -67
  72. package/src/adapter/Runtime/AggregateRuntime_Builder_PerAggregate_Async.res +44 -67
  73. package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res +53 -82
  74. package/src/adapter/Runtime/AggregateRuntime_Builder_Single_Async.res +47 -77
  75. package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res +18 -11
  76. package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res +80 -97
  77. package/src/adapter/Runtime/CounterEntryPoint_Ops.res +2 -7
  78. package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res +16 -10
  79. package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res +24 -15
  80. package/src/adapter/Runtime/EventCollectorRuntime_Builder_PerEventCollector.res +17 -37
  81. package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res +31 -57
  82. package/src/adapter/Runtime/EventMapperEntryPoint_Ops.res +18 -24
  83. package/src/adapter/Runtime/ExtensionPointEntryPoint_Ops.res +1 -5
  84. package/src/adapter/Runtime/ExtensionPointRuntime_Builder_PerExtensionPoint.res +4 -3
  85. package/src/adapter/Runtime/HeartbeatEntryPoint.res +0 -1
  86. package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.res +4 -2
  87. package/src/adapter/Runtime/PgMigrationEntryPoint.res +0 -1
  88. package/src/adapter/Runtime/PgQueryResolverEntryPoint_Ops.res +3 -2
  89. package/src/adapter/Runtime/PluginExtensionPointEntryPoint.res +1 -2
  90. package/src/adapter/Runtime/ProjectionEntryPoint_Ops.res +4 -5
  91. package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res +22 -10
  92. package/src/adapter/Runtime/RuntimeExtensionEntryPoint_Ops.res +8 -2
  93. package/src/adapter/Runtime/SideEffectHandlerRuntime_Builder_Single.res +14 -31
  94. package/src/adapter/Runtime/StateChangeSliceRuntime_Builder_Single.res +16 -17
  95. package/src/adapter/Runtime/StateViewSliceEntryPoint_Ops.res +10 -9
  96. package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res +46 -52
  97. package/src/adapter/Runtime/StreamRoutedEntryPoint_Ops.res +36 -41
  98. package/src/adapter/Runtime/TaskBucketEntryPoint_Ops.res +31 -37
  99. package/src/adapter/Runtime/TaskRuntime_Builder_PerBucket.res +6 -7
  100. package/src/adapter/ScheduledPublisher/ScheduledPublisher_CloudWatchEvents.res +4 -6
  101. package/src/adapter/ScheduledPublisher/ScheduledPublisher_CloudWatchEvents_Runtime.res +2 -6
  102. package/src/adapter/StateTopic/StateTopic_AppSync.res +21 -16
  103. package/src/adapter/StateTopic/StateTopic_AppSync_Ops.res +36 -32
  104. package/src/adapter/Task/TaskBucket_S3.res +6 -1
  105. package/src/adapter/Upload/Upload_Claim_S3.res +17 -20
  106. package/src/adapter/Upload/Upload_Claim_S3_Ops.res +5 -2
  107. package/src/adapter/Upload/Upload_Presign_S3.res +51 -49
  108. package/src/capability/Capability_Geocoding_AwsLocation.res +4 -1
  109. package/src/capability/Capability_Messaging.res +4 -5
  110. package/src/capability/Capability_Messaging_Ses.res +3 -1
  111. package/src/capability/Capability_ObjectStore_S3.res +1 -7
  112. package/src/components/Api/AppSync_Adapter.res +28 -41
  113. package/src/components/Api/AppSync_SdlDecorate.res +38 -30
  114. package/src/components/AutomationSlice_Builder.res +6 -4
  115. package/src/components/ExtensionPoint_Builder.res +9 -10
  116. package/src/components/InboundTranslationSlice_Builder.res +7 -8
  117. package/src/components/OutboundTranslationSlice_Builder.res +14 -8
  118. package/src/components/OutboundTranslationSlice_Builder.res.mjs +1 -1
  119. package/src/components/SideEffectHandler_Single.res +4 -4
  120. package/src/components/StateViewSlice_Builder.res +6 -4
  121. package/src/components/StateViewSlice_Builder_Stream.res +6 -4
  122. package/src/components/Task_Builder_PerBucket.res +10 -11
  123. package/src/plugin/cloner/ClonerRunner_Fargate.res +3 -10
  124. package/src/plugin/cloner/ClonerRunner_Fargate_Runtime.res +1 -4
  125. package/src/plugin/heartbeat/HeartbeatRunner_CloudWatchEvents.res +5 -5
  126. package/src/plugin/runtime/PluginExtensionPointRuntime_Builder.res +19 -21
  127. package/src/plugin/runtime/PluginRuntime_Builder.res +167 -188
  128. package/src/plugin/runtime/Util_PluginMessage_Runtime.res +6 -5
  129. package/src/plugin/stack/Plugin_Stack.res +104 -103
  130. package/src/util/Util_AlarmSpec.res +2 -1
  131. package/src/util/Util_AppSync.res +8 -2
  132. package/src/util/Util_AppSync_Caller.res +15 -14
  133. package/src/util/Util_Bundle.res +46 -42
  134. package/src/util/Util_CognitoGroupUser_Runtime.res +32 -34
  135. package/src/util/Util_Cognito_Runtime.res +11 -7
  136. package/src/util/Util_DeadLetterQueue.res +12 -2
  137. package/src/util/Util_DynamoDb.res +5 -3
  138. package/src/util/Util_DynamoDbStream.res +7 -3
  139. package/src/util/Util_DynamoDbStream_Runtime.res +1 -2
  140. package/src/util/Util_DynamoDb_Runtime.res +23 -41
  141. package/src/util/Util_HostUiDomain.res +6 -1
  142. package/src/util/Util_Kinesis_Runtime.res +10 -11
  143. package/src/util/Util_Lambda.res +12 -5
  144. package/src/util/Util_LambdaEnvBudget.res +5 -5
  145. package/src/util/Util_LambdaLogging.res +1 -2
  146. package/src/util/Util_LogGroup_Adopting.res +1 -4
  147. package/src/util/Util_ResourceNaming.res +1 -2
  148. package/src/util/Util_SNS.res +8 -3
  149. package/src/util/Util_SNS_FIFO.res +1 -1
  150. package/src/util/Util_SNS_Runtime.res +11 -10
  151. package/src/util/Util_SQS.res +4 -2
  152. package/src/util/Util_SQS_FIFO.res +5 -2
  153. package/src/util/Util_SQS_Runtime.res +37 -36
  154. package/src/util/Util_ShellConfig.res +3 -2
  155. package/src/util/Util_StaticBundle.res +1 -3
  156. package/src/util/Util_StoreLayout.res +4 -1
  157. package/tests/AWS_TagsTest.res +16 -5
  158. package/tests/AppSyncEventsSigner_OpsTest.res +11 -2
  159. package/tests/AppSync_AdapterTest.res +203 -224
  160. package/tests/AppSync_Resolver_RetryingTest.res +64 -44
  161. package/tests/AppSync_RetirementNarrowingTest.res +59 -46
  162. package/tests/AppSync_SdlDecorateTest.res +22 -20
  163. package/tests/AppSync_SourceApiAssociation_RetryingTest.res +40 -22
  164. package/tests/Auth_ActiveRolePoolAttachmentTest.res +114 -107
  165. package/tests/Auth_ActiveRoleStoreTest.res +12 -9
  166. package/tests/Auth_ActiveRoleStore_SchemaTest.res +2 -1
  167. package/tests/Auth_ActiveRoleTrigger_OpsTest.res +20 -16
  168. package/tests/Auth_CognitoTest.res +31 -46
  169. package/tests/AutomationSliceEntryPoint_OpsTest.res +69 -67
  170. package/tests/BakeConvergenceTest.res +22 -25
  171. package/tests/Capability_MessagingTest.res +28 -12
  172. package/tests/CommandTopicQueuePolicyTest.res +1 -2
  173. package/tests/CounterEntryPoint_OpsTest.res +3 -7
  174. package/tests/DcbEventLogStorage_DynamoDb_RuntimeTest.res +306 -210
  175. package/tests/EventCollectorConnectLambdaPreviewTest.res +14 -7
  176. package/tests/EventCollectorEntryPoint_OpsTest.res +12 -4
  177. package/tests/EventCollectorPluginDefinitionAssetTest.res +18 -11
  178. package/tests/EventCollectorReadyGateTest.res +5 -4
  179. package/tests/EventLogStorage_DynamoDb_RuntimeTest.res +1 -2
  180. package/tests/EventMapperEntryPoint_OpsTest.res +1 -3
  181. package/tests/ExtensionPointEntryPoint_OpsTest.res +1 -4
  182. package/tests/HandlerFactoryDispatchTest.res +97 -70
  183. package/tests/MCP_LambdaTest.res +16 -27
  184. package/tests/PgChangeFeedRelay_IntegrationTest.res +123 -93
  185. package/tests/PgChangeFeedRelay_RuntimeTest.res +13 -10
  186. package/tests/PgMigrationEntryPoint_IntegrationTest.res +0 -1
  187. package/tests/PgPipeline_IntegrationTest.res +80 -66
  188. package/tests/PgQueryDbUnion_IntegrationTest.res +1 -2
  189. package/tests/PgQueryResolverEntryPoint_OpsTest.res +2 -6
  190. package/tests/PgQueryResolver_LambdaTest.res +41 -24
  191. package/tests/Platform_AdminScan_OpsTest.res +4 -1
  192. package/tests/Platform_ComponentDefinitions_Lambda_OpsTest.res +137 -129
  193. package/tests/ProjectionEntryPoint_DisplayNameTest.res +7 -3
  194. package/tests/ProjectionEntryPoint_UnionStampTest.res +3 -2
  195. package/tests/ProvisionIdentityTest.res +2 -6
  196. package/tests/QueryDbOwnerIndexTableTest.res +6 -4
  197. package/tests/QueryDbResolvers_AppSyncTest.res +23 -16
  198. package/tests/QueryInterceptor_LambdaTest.res +11 -13
  199. package/tests/ReadModelEntryPoint_OpsTest.res +43 -41
  200. package/tests/RuntimeExtensionEntryPoint_OpsTest.res +4 -3
  201. package/tests/SideEffectEntryPoint_OpsTest.res +23 -18
  202. package/tests/StateChangeDescriptorParityTest.res +28 -26
  203. package/tests/StateTopicKeySchemaTest.res +18 -15
  204. package/tests/StateViewSliceEntryPoint_OpsTest.res +55 -44
  205. package/tests/TaskBucketEntryPoint_OpsTest.res +13 -13
  206. package/tests/Upload_ClaimTest.res +3 -3
  207. package/tests/Util_AwsErrorTest.res +4 -7
  208. package/tests/Util_BundleDependencyClosureTest.res +15 -3
  209. package/tests/Util_BundleEsmLoaderTest.res +3 -3
  210. package/tests/Util_BundleResolveTest.res +4 -6
  211. package/tests/Util_BundleRuntimeExtensionTest.res +17 -18
  212. package/tests/Util_DcbMetricsTest.res +6 -6
  213. package/tests/Util_DynamoDbStreamTest.res +4 -5
  214. package/tests/Util_DynamoDbTest.res +3 -2
  215. package/tests/Util_LambdaEnvBudgetTest.res +22 -18
  216. package/tests/Util_LambdaLoggingTest.res +9 -8
  217. package/tests/Util_LogGroup_AdoptingTest.res +10 -5
  218. package/tests/Util_LogRetentionTest.res +6 -2
  219. package/tests/Util_OwnerScopeEnvTest.res +10 -8
  220. package/tests/Util_SQS_RuntimeTest.res +5 -7
  221. package/tests/Util_ShellConfigTest.res +7 -17
  222. package/tests/Util_StaticBundleTest.res +3 -8
  223. package/tests/Util_StoreLayoutTest.res +17 -12
  224. package/tests/integration/AggregateEntryPoint_IntegrationTest.res +1 -4
  225. package/tests/integration/DcbCommandTopicEntryPoint_IntegrationTest.res +15 -15
  226. package/tests/integration/DcbEventLogStorage_DynamoDb_IntegrationTest.res +296 -232
  227. package/tests/integration/DcbIntegrationHarness.res +8 -8
  228. package/tests/integration/EpCompositeSlice.res +16 -8
  229. package/tests/integration/QueryDbUnion_DynamoDb_IntegrationTest.res +8 -10
  230. package/tests/integration/StateViewSliceEntryPoint_IntegrationTest.res +10 -10
  231. package/tests/integration/SvsTestSlice_Projection.res +1 -3
  232. package/tests/util/Util_AlarmSpecTest.res +62 -49
@@ -32,7 +32,10 @@ let stored = (eventType, tags, data): DcbEventLog_Adapter.rawStoredEvent => {
32
32
  let capturingSendBatch = sink => async jsons => sink := sink.contents->Array.concat(jsons)
33
33
 
34
34
  let idOf = json =>
35
- json->JSON.Decode.object->Option.flatMap(o => o->Dict.get("id"))->Option.flatMap(JSON.Decode.string)
35
+ json
36
+ ->JSON.Decode.object
37
+ ->Option.flatMap(o => o->Dict.get("id"))
38
+ ->Option.flatMap(JSON.Decode.string)
36
39
 
37
40
  switch NodeProcess.env->Dict.get("PG_URL") {
38
41
  | None =>
@@ -63,7 +66,11 @@ switch NodeProcess.env->Dict.get("PG_URL") {
63
66
  testPromise("relays each appended DCB event as an {id, meta, event} body", async () => {
64
67
  let (_n, ops, _s) = makeLog("relay-basic")
65
68
  let mk = i =>
66
- stored("OrderPlaced", [{key: "orderId", value: "o-1"}], jsonObj([("i", JSON.Encode.int(i))]))
69
+ stored(
70
+ "OrderPlaced",
71
+ [{key: "orderId", value: "o-1"}],
72
+ jsonObj([("i", JSON.Encode.int(i))]),
73
+ )
67
74
  let _ = await ops.append([mk(0), mk(1), mk(2)])
68
75
 
69
76
  let sink = ref([])
@@ -81,109 +88,134 @@ switch NodeProcess.env->Dict.get("PG_URL") {
81
88
  let first = sink.contents->Array.getUnsafe(0)
82
89
  expect(idOf(first))->toEqual(Some("orderId:o-1"))
83
90
  let obj = first->JSON.Decode.object->Option.getOrThrow
84
- expect(obj->Dict.get("event")->Option.getOrThrow->JSON.stringify->String.includes("OrderPlaced"))->toBe(true)
91
+ expect(
92
+ obj->Dict.get("event")->Option.getOrThrow->JSON.stringify->String.includes("OrderPlaced"),
93
+ )->toBe(true)
85
94
  let metaObj = obj->Dict.get("meta")->Option.flatMap(JSON.Decode.object)->Option.getOrThrow
86
95
  expect(metaObj->Dict.get("service"))->toEqual(Some(JSON.Encode.string("relay-it")))
87
96
  })
88
97
 
89
- testPromise("checkpoints — a second drain from the same subscriber sees nothing new", async () => {
90
- let (_n, ops, _s) = makeLog("relay-checkpoint")
91
- let mk = i => stored("Fed", [{key: "orderId", value: "f"}], jsonObj([("i", JSON.Encode.int(i))]))
92
- let _ = await ops.append([mk(0), mk(1)])
98
+ testPromise(
99
+ "checkpoints — a second drain from the same subscriber sees nothing new",
100
+ async () => {
101
+ let (_n, ops, _s) = makeLog("relay-checkpoint")
102
+ let mk = i =>
103
+ stored("Fed", [{key: "orderId", value: "f"}], jsonObj([("i", JSON.Encode.int(i))]))
104
+ let _ = await ops.append([mk(0), mk(1)])
93
105
 
94
- let sink = ref([])
95
- let first = await PgChangeFeedRelay_Runtime.relayWithPool(
96
- ~pool,
97
- ~logName="relay-checkpoint",
98
- ~subscriber="relay-it",
99
- ~sendBatch=capturingSendBatch(sink),
100
- )
101
- expect(first)->toBe(2)
106
+ let sink = ref([])
107
+ let first = await PgChangeFeedRelay_Runtime.relayWithPool(
108
+ ~pool,
109
+ ~logName="relay-checkpoint",
110
+ ~subscriber="relay-it",
111
+ ~sendBatch=capturingSendBatch(sink),
112
+ )
113
+ expect(first)->toBe(2)
102
114
 
103
- let second = await PgChangeFeedRelay_Runtime.relayWithPool(
104
- ~pool,
105
- ~logName="relay-checkpoint",
106
- ~subscriber="relay-it",
107
- ~sendBatch=capturingSendBatch(sink),
108
- )
109
- expect(second)->toBe(0)
110
- // No extra bodies were emitted on the second pass.
111
- expect(sink.contents->Array.length)->toBe(2)
112
- })
115
+ let second = await PgChangeFeedRelay_Runtime.relayWithPool(
116
+ ~pool,
117
+ ~logName="relay-checkpoint",
118
+ ~subscriber="relay-it",
119
+ ~sendBatch=capturingSendBatch(sink),
120
+ )
121
+ expect(second)->toBe(0)
122
+ // No extra bodies were emitted on the second pass.
123
+ expect(sink.contents->Array.length)->toBe(2)
124
+ },
125
+ )
113
126
 
114
- testPromise("classic: relays each appended event_log event as an {id, meta, event} body", async () => {
115
- let (_n, ops, _s) =
116
- ReventlessPostgres.EventLogStorage_Postgres.makeStorage(
127
+ testPromise(
128
+ "classic: relays each appended event_log event as an {id, meta, event} body",
129
+ async () => {
130
+ let (_n, ops, _s) = ReventlessPostgres.EventLogStorage_Postgres.makeStorage(
117
131
  ~pool,
118
132
  ~name="ClassicRelayBasicEventLog",
119
133
  ~opts=(),
120
134
  )
121
- // The flat on-disk item the classic append stores verbatim — same shape the
122
- // DynamoDB path puts (id, position, event, data, decomposed meta).
123
- let flatItem = (~id, ~seq, ~eventType, ~data) =>
124
- [
125
- ("id", JSON.Encode.string(id)),
126
- ("position", JSON.Encode.string(seq->Int.toString->String.padStart(9, "0"))),
127
- ("event", JSON.Encode.string(eventType)),
128
- ("data", data),
129
- ]
130
- ->Array.concat(
131
- ReventlessCore.Message.generateMeta(~service="relay-it")->ReventlessCore.Message.decomposeMeta,
135
+ // The flat on-disk item the classic append stores verbatim — same shape the
136
+ // DynamoDB path puts (id, position, event, data, decomposed meta).
137
+ let flatItem = (~id, ~seq, ~eventType, ~data) =>
138
+ [
139
+ ("id", JSON.Encode.string(id)),
140
+ ("position", JSON.Encode.string(seq->Int.toString->String.padStart(9, "0"))),
141
+ ("event", JSON.Encode.string(eventType)),
142
+ ("data", data),
143
+ ]
144
+ ->Array.concat(
145
+ ReventlessCore.Message.generateMeta(
146
+ ~service="relay-it",
147
+ )->ReventlessCore.Message.decomposeMeta,
148
+ )
149
+ ->Dict.fromArray
150
+ ->JSON.Encode.object
151
+ let _ = await ops.append(
152
+ 0,
153
+ "agg-1",
154
+ [
155
+ flatItem(
156
+ ~id="agg-1",
157
+ ~seq=0,
158
+ ~eventType="NameUpdated",
159
+ ~data=jsonObj([("n", JSON.Encode.int(0))]),
160
+ ),
161
+ flatItem(
162
+ ~id="agg-1",
163
+ ~seq=1,
164
+ ~eventType="NameUpdated",
165
+ ~data=jsonObj([("n", JSON.Encode.int(1))]),
166
+ ),
167
+ ],
132
168
  )
133
- ->Dict.fromArray
134
- ->JSON.Encode.object
135
- let _ = await ops.append(
136
- 0,
137
- "agg-1",
138
- [
139
- flatItem(~id="agg-1", ~seq=0, ~eventType="NameUpdated", ~data=jsonObj([("n", JSON.Encode.int(0))])),
140
- flatItem(~id="agg-1", ~seq=1, ~eventType="NameUpdated", ~data=jsonObj([("n", JSON.Encode.int(1))])),
141
- ],
142
- )
143
169
 
144
- let sink = ref([])
145
- let processed = await PgChangeFeedRelay_Runtime.relayClassicWithPool(
146
- ~pool,
147
- ~logName="ClassicRelayBasicEventLog",
148
- ~subscriber="relay-it:ClassicRelayBasicEventLog",
149
- ~sendBatch=capturingSendBatch(sink),
150
- )
170
+ let sink = ref([])
171
+ let processed = await PgChangeFeedRelay_Runtime.relayClassicWithPool(
172
+ ~pool,
173
+ ~logName="ClassicRelayBasicEventLog",
174
+ ~subscriber="relay-it:ClassicRelayBasicEventLog",
175
+ ~sendBatch=capturingSendBatch(sink),
176
+ )
151
177
 
152
- expect(processed)->toBe(2)
153
- expect(sink.contents->Array.length)->toBe(2)
154
- let first = sink.contents->Array.getUnsafe(0)
155
- expect(idOf(first))->toEqual(Some("agg-1"))
156
- let obj = first->JSON.Decode.object->Option.getOrThrow
157
- expect(
158
- obj->Dict.get("event")->Option.getOrThrow->JSON.stringify->String.includes("NameUpdated"),
159
- )->toBe(true)
160
- let metaObj = obj->Dict.get("meta")->Option.flatMap(JSON.Decode.object)->Option.getOrThrow
161
- expect(metaObj->Dict.get("service"))->toEqual(Some(JSON.Encode.string("relay-it")))
178
+ expect(processed)->toBe(2)
179
+ expect(sink.contents->Array.length)->toBe(2)
180
+ let first = sink.contents->Array.getUnsafe(0)
181
+ expect(idOf(first))->toEqual(Some("agg-1"))
182
+ let obj = first->JSON.Decode.object->Option.getOrThrow
183
+ expect(
184
+ obj->Dict.get("event")->Option.getOrThrow->JSON.stringify->String.includes("NameUpdated"),
185
+ )->toBe(true)
186
+ let metaObj = obj->Dict.get("meta")->Option.flatMap(JSON.Decode.object)->Option.getOrThrow
187
+ expect(metaObj->Dict.get("service"))->toEqual(Some(JSON.Encode.string("relay-it")))
162
188
 
163
- // Checkpointed: a second drain from the same subscriber sees nothing new.
164
- let second = await PgChangeFeedRelay_Runtime.relayClassicWithPool(
165
- ~pool,
166
- ~logName="ClassicRelayBasicEventLog",
167
- ~subscriber="relay-it:ClassicRelayBasicEventLog",
168
- ~sendBatch=capturingSendBatch(sink),
169
- )
170
- expect(second)->toBe(0)
171
- expect(sink.contents->Array.length)->toBe(2)
172
- })
189
+ // Checkpointed: a second drain from the same subscriber sees nothing new.
190
+ let second = await PgChangeFeedRelay_Runtime.relayClassicWithPool(
191
+ ~pool,
192
+ ~logName="ClassicRelayBasicEventLog",
193
+ ~subscriber="relay-it:ClassicRelayBasicEventLog",
194
+ ~sendBatch=capturingSendBatch(sink),
195
+ )
196
+ expect(second)->toBe(0)
197
+ expect(sink.contents->Array.length)->toBe(2)
198
+ },
199
+ )
173
200
 
174
201
  testPromise("classic: per-log subscribers keep checkpoints isolated across logs", async () => {
175
202
  // event_log_subscription keys by subscriber alone. With a SHARED subscriber,
176
203
  // draining log A after log B's events were appended would save a cursor past
177
204
  // B's rows and silently skip them — the clobber the per-log subscriber fixes.
178
205
  let mkOps = name => {
179
- let (_n, ops, _s) =
180
- ReventlessPostgres.EventLogStorage_Postgres.makeStorage(~pool, ~name, ~opts=())
206
+ let (_n, ops, _s) = ReventlessPostgres.EventLogStorage_Postgres.makeStorage(
207
+ ~pool,
208
+ ~name,
209
+ ~opts=(),
210
+ )
181
211
  ops
182
212
  }
183
213
  let opsB = mkOps("ClassicRelayIsoBEventLog")
184
214
  let opsA = mkOps("ClassicRelayIsoAEventLog")
185
215
  let meta = () =>
186
- ReventlessCore.Message.generateMeta(~service="relay-it")->ReventlessCore.Message.decomposeMeta
216
+ ReventlessCore.Message.generateMeta(
217
+ ~service="relay-it",
218
+ )->ReventlessCore.Message.decomposeMeta
187
219
  let item = (id, eventType) =>
188
220
  [
189
221
  ("id", JSON.Encode.string(id)),
@@ -221,20 +253,18 @@ switch NodeProcess.env->Dict.get("PG_URL") {
221
253
  })
222
254
 
223
255
  testPromise("partitionTag pins the id to its tag on a multi-tag event", async () => {
224
- let (_n, ops, _s) =
225
- ReventlessPostgres.DcbEventLogStorage_Postgres.makeStorage(
226
- ~pool,
227
- ~name="relay-pt",
228
- ~indexes=[],
229
- ~partitionTag=DcbTag.Simple({key: "customerId"}),
230
- ~opts,
231
- )
232
- let ev =
233
- stored(
234
- "OrderPlaced",
235
- [{key: "orderId", value: "o-9"}, {key: "customerId", value: "c-9"}],
236
- jsonObj([("n", JSON.Encode.int(1))]),
237
- )
256
+ let (_n, ops, _s) = ReventlessPostgres.DcbEventLogStorage_Postgres.makeStorage(
257
+ ~pool,
258
+ ~name="relay-pt",
259
+ ~indexes=[],
260
+ ~partitionTag=DcbTag.Simple({key: "customerId"}),
261
+ ~opts,
262
+ )
263
+ let ev = stored(
264
+ "OrderPlaced",
265
+ [{key: "orderId", value: "o-9"}, {key: "customerId", value: "c-9"}],
266
+ jsonObj([("n", JSON.Encode.int(1))]),
267
+ )
238
268
  let _ = await ops.append([ev])
239
269
 
240
270
  // The relay receives the sury-encoded partition tag exactly as the builder emits it.
@@ -30,8 +30,7 @@ describe("PgChangeFeedRelay_Runtime.toEventCollectorJson", () => {
30
30
  expect(eventJson->JSON.stringify->String.includes("c-1"))->toBe(true)
31
31
 
32
32
  // meta round-trips the producing service
33
- let metaObj =
34
- obj->Dict.get("meta")->Option.flatMap(JSON.Decode.object)->Option.getOrThrow
33
+ let metaObj = obj->Dict.get("meta")->Option.flatMap(JSON.Decode.object)->Option.getOrThrow
35
34
  expect(metaObj->Dict.get("service"))->toEqual(Some(JSON.Encode.string("Orders")))
36
35
  })
37
36
 
@@ -52,7 +51,10 @@ describe("PgChangeFeedRelay_Runtime partitionTag (B2.3d)", () => {
52
51
  testSync("a Simple partitionTag selects its tag for the id, not just the first", () => {
53
52
  let pt = Reventless.DcbTag.Simple({key: "customerId"})
54
53
  let json =
55
- PgChangeFeedRelay_Runtime.toEventCollectorJson(multiTagEvent, ~partitionTag=pt)->Option.getOrThrow
54
+ PgChangeFeedRelay_Runtime.toEventCollectorJson(
55
+ multiTagEvent,
56
+ ~partitionTag=pt,
57
+ )->Option.getOrThrow
56
58
  let obj = json->JSON.Decode.object->Option.getOrThrow
57
59
  // Without partitionTag the id would be "orderId:o-1" (first tag); the tag
58
60
  // pins it to "customerId:c-9".
@@ -64,12 +66,13 @@ describe("PgChangeFeedRelay_Runtime partitionTag (B2.3d)", () => {
64
66
  // and PgChangeFeedRelay_Runtime.relay parses it back the same way. Guard the
65
67
  // round-trip so a schema change can't silently break the id derivation.
66
68
  let simple = Reventless.DcbTag.Simple({key: "courseId"})
67
- let composite =
68
- Reventless.DcbTag.Composite({keys: ["studentId", "courseId"], seps: [":"]})
69
- [simple, composite]->Array.forEach(pt => {
70
- let wire = pt->Reventless.Util_Sury.toJson(Reventless.DcbTag.derivedPartitionTagSchema)
71
- let back = wire->Reventless.Util_Sury.fromJson(Reventless.DcbTag.derivedPartitionTagSchema)
72
- expect(back)->toEqual(pt)
73
- })
69
+ let composite = Reventless.DcbTag.Composite({keys: ["studentId", "courseId"], seps: [":"]})
70
+ [simple, composite]->Array.forEach(
71
+ pt => {
72
+ let wire = pt->Reventless.Util_Sury.toJson(Reventless.DcbTag.derivedPartitionTagSchema)
73
+ let back = wire->Reventless.Util_Sury.fromJson(Reventless.DcbTag.derivedPartitionTagSchema)
74
+ expect(back)->toEqual(pt)
75
+ },
76
+ )
74
77
  })
75
78
  })
@@ -14,7 +14,6 @@
14
14
 
15
15
  open JestGlobals
16
16
 
17
-
18
17
  // Drive the real ReScript `runMigration` with the pool injected — the guard
19
18
  // rejects a HANDLER_CONFIG with no pgConnection; the real fields below are
20
19
  // ignored because `~makePool` is injected. Returns "ok" on success.
@@ -15,7 +15,6 @@
15
15
  open JestGlobals
16
16
  open ReventlessCore
17
17
 
18
-
19
18
  // --- Fixtures: one classic source, one read model, one projection mapping ---
20
19
 
21
20
  module OrderSource = {
@@ -132,78 +131,93 @@ switch NodeProcess.env->Dict.get("PG_URL") {
132
131
  let _ = pool->ReventlessPostgres.PgDriver.endPool
133
132
  })
134
133
 
135
- describe("Postgres pipeline (append → relay → feed decode → projection → qdb row)", () => {
136
- testPromise("projects a classic event end-to-end into the Postgres QueryDb", async () => {
137
- let (_n, elOps, _s) =
138
- ReventlessPostgres.EventLogStorage_Postgres.makeStorage(
134
+ describe(
135
+ "Postgres pipeline (append → relay → feed decode → projection → qdb row)",
136
+ () => {
137
+ testPromise("projects a classic event end-to-end into the Postgres QueryDb", async () => {
138
+ let (_n, elOps, _s) = ReventlessPostgres.EventLogStorage_Postgres.makeStorage(
139
139
  ~pool,
140
140
  ~name="PipelineOrderEventLog",
141
141
  ~opts=(),
142
142
  )
143
- let _ = await elOps.append(
144
- 0,
145
- "o-1",
146
- [flatItem(~id="o-1", ~seq=0, ~service=OrderSource.name, ~event=OrderPlaced({product: "boat"}))],
147
- )
148
-
149
- // Relay drains event_log and emits the EventCollector bodies.
150
- let sink = ref([])
151
- let relayed = await PgChangeFeedRelay_Runtime.relayClassicWithPool(
152
- ~pool,
153
- ~logName="PipelineOrderEventLog",
154
- ~subscriber="aws-rm-feed:PipelineOrderEventLog",
155
- ~sendBatch=async jsons => sink := sink.contents->Array.concat(jsons),
156
- )
157
- expect(relayed)->toBe(1)
158
-
159
- // Feed-queue delivery → entry-point decode → real projection callback.
160
- let _ = await EventCollectorChannel_DynamoDbStream_Runtime.handleStreamEvent(
161
- Callback.handleJsonEvents,
162
- asSqsEvent(sink.contents),
163
- (),
164
- )->Effect.runPromise
165
-
166
- // The projected row is in the Postgres QueryDb.
167
- switch await qdbOps.load("o-1") {
168
- | Ok(rows) =>
169
- expect(rows->Array.length)->toBe(1)
170
- let row = rows->Array.getUnsafe(0)->JSON.Decode.object->Option.getOrThrow
171
- expect(row->Dict.get("product"))->toEqual(Some(JSON.Encode.string("boat")))
172
- expect(row->Dict.get("id"))->toEqual(Some(JSON.Encode.string("o-1")))
173
- | Error(_) => expect("load failed")->toBe("Ok")
174
- }
175
- })
143
+ let _ = await elOps.append(
144
+ 0,
145
+ "o-1",
146
+ [
147
+ flatItem(
148
+ ~id="o-1",
149
+ ~seq=0,
150
+ ~service=OrderSource.name,
151
+ ~event=OrderPlaced({product: "boat"}),
152
+ ),
153
+ ],
154
+ )
176
155
 
177
- testPromise("meta.service mismatch is a silent no-op (dispatch filter)", async () => {
178
- let (_n, elOps, _s) =
179
- ReventlessPostgres.EventLogStorage_Postgres.makeStorage(
156
+ // Relay drains event_log and emits the EventCollector bodies.
157
+ let sink = ref([])
158
+ let relayed = await PgChangeFeedRelay_Runtime.relayClassicWithPool(
159
+ ~pool,
160
+ ~logName="PipelineOrderEventLog",
161
+ ~subscriber="aws-rm-feed:PipelineOrderEventLog",
162
+ ~sendBatch=async jsons => sink := sink.contents->Array.concat(jsons),
163
+ )
164
+ expect(relayed)->toBe(1)
165
+
166
+ // Feed-queue delivery → entry-point decode → real projection callback.
167
+ let _ = await EventCollectorChannel_DynamoDbStream_Runtime.handleStreamEvent(
168
+ Callback.handleJsonEvents,
169
+ asSqsEvent(sink.contents),
170
+ (),
171
+ )->Effect.runPromise
172
+
173
+ // The projected row is in the Postgres QueryDb.
174
+ switch await qdbOps.load("o-1") {
175
+ | Ok(rows) =>
176
+ expect(rows->Array.length)->toBe(1)
177
+ let row = rows->Array.getUnsafe(0)->JSON.Decode.object->Option.getOrThrow
178
+ expect(row->Dict.get("product"))->toEqual(Some(JSON.Encode.string("boat")))
179
+ expect(row->Dict.get("id"))->toEqual(Some(JSON.Encode.string("o-1")))
180
+ | Error(_) => expect("load failed")->toBe("Ok")
181
+ }
182
+ })
183
+
184
+ testPromise("meta.service mismatch is a silent no-op (dispatch filter)", async () => {
185
+ let (_n, elOps, _s) = ReventlessPostgres.EventLogStorage_Postgres.makeStorage(
180
186
  ~pool,
181
187
  ~name="PipelineWrongServiceEventLog",
182
188
  ~opts=(),
183
189
  )
184
- let _ = await elOps.append(
185
- 0,
186
- "o-2",
187
- [flatItem(~id="o-2", ~seq=0, ~service="NotTheSource", ~event=OrderPlaced({product: "x"}))],
188
- )
189
-
190
- let sink = ref([])
191
- let _ = await PgChangeFeedRelay_Runtime.relayClassicWithPool(
192
- ~pool,
193
- ~logName="PipelineWrongServiceEventLog",
194
- ~subscriber="aws-rm-feed:PipelineWrongServiceEventLog",
195
- ~sendBatch=async jsons => sink := sink.contents->Array.concat(jsons),
196
- )
197
- let _ = await EventCollectorChannel_DynamoDbStream_Runtime.handleStreamEvent(
198
- Callback.handleJsonEvents,
199
- asSqsEvent(sink.contents),
200
- (),
201
- )->Effect.runPromise
202
-
203
- switch await qdbOps.load("o-2") {
204
- | Ok(rows) => expect(rows->Array.length)->toBe(0)
205
- | Error(_) => expect("load failed")->toBe("Ok")
206
- }
207
- })
208
- })
190
+ let _ = await elOps.append(
191
+ 0,
192
+ "o-2",
193
+ [
194
+ flatItem(
195
+ ~id="o-2",
196
+ ~seq=0,
197
+ ~service="NotTheSource",
198
+ ~event=OrderPlaced({product: "x"}),
199
+ ),
200
+ ],
201
+ )
202
+
203
+ let sink = ref([])
204
+ let _ = await PgChangeFeedRelay_Runtime.relayClassicWithPool(
205
+ ~pool,
206
+ ~logName="PipelineWrongServiceEventLog",
207
+ ~subscriber="aws-rm-feed:PipelineWrongServiceEventLog",
208
+ ~sendBatch=async jsons => sink := sink.contents->Array.concat(jsons),
209
+ )
210
+ let _ = await EventCollectorChannel_DynamoDbStream_Runtime.handleStreamEvent(
211
+ Callback.handleJsonEvents,
212
+ asSqsEvent(sink.contents),
213
+ (),
214
+ )->Effect.runPromise
215
+
216
+ switch await qdbOps.load("o-2") {
217
+ | Ok(rows) => expect(rows->Array.length)->toBe(0)
218
+ | Error(_) => expect("load failed")->toBe("Ok")
219
+ }
220
+ })
221
+ },
222
+ )
209
223
  }
@@ -92,8 +92,7 @@ switch NodeProcess.env->Dict.get("PG_URL") {
92
92
  }
93
93
 
94
94
  switch await Ops.load("c-1"->CustomersSpec.Id.makeFromString) {
95
- | Ok([{geolocation: Located({lat, lng})}]) =>
96
- expect((lat, lng))->toEqual((48.2082, 16.3738))
95
+ | Ok([{geolocation: Located({lat, lng})}]) => expect((lat, lng))->toEqual((48.2082, 16.3738))
97
96
  | _ => expect("round trip")->toBe("Located with its point intact")
98
97
  }
99
98
  })
@@ -15,16 +15,12 @@ describe("PgQueryResolverEntryPoint_Ops.parseResolverConfig", () => {
15
15
  })
16
16
 
17
17
  testSync("null pgConnection decodes to None", () => {
18
- let config = PgQueryResolverEntryPoint_Ops.parseResolverConfig(
19
- `{"pgConnection":null,"handlers":[]}`,
20
- )
18
+ let config = PgQueryResolverEntryPoint_Ops.parseResolverConfig(`{"pgConnection":null,"handlers":[]}`)
21
19
  expect(config.pgConnection->Option.isNone)->toBe(true)
22
20
  })
23
21
 
24
22
  testSync("decodes connection, handlers, and node types", () => {
25
- let config = PgQueryResolverEntryPoint_Ops.parseResolverConfig(
26
- `{"pgConnection":{"host":"db.local","port":5432,"database":"app","username":"master","secretArn":"arn:secret"},"handlers":[{"readModelName":"Products","specModule":"@x/p/src/ReadModel/Products.res.mjs","labelField":"name","includeIdParam":true},{"readModelName":"Orders","specModule":"@x/p/src/ReadModel/Orders.res.mjs","labelField":"id","includeIdParam":false}],"nodeTypes":{"Product":"Products"}}`,
27
- )
23
+ let config = PgQueryResolverEntryPoint_Ops.parseResolverConfig(`{"pgConnection":{"host":"db.local","port":5432,"database":"app","username":"master","secretArn":"arn:secret"},"handlers":[{"readModelName":"Products","specModule":"@x/p/src/ReadModel/Products.res.mjs","labelField":"name","includeIdParam":true},{"readModelName":"Orders","specModule":"@x/p/src/ReadModel/Orders.res.mjs","labelField":"id","includeIdParam":false}],"nodeTypes":{"Product":"Products"}}`)
28
24
  expect(config.pgConnection->Option.map(cc => cc.host))->toEqual(Some("db.local"))
29
25
  expect(config.handlers->Array.length)->toBe(2)
30
26
  let first = config.handlers->Array.getUnsafe(0)