@reventlessdev/reventless-aws 3.0.0-alpha.343 → 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 (234) hide show
  1. package/CHANGELOG.md +15 -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 +144 -67
  134. package/src/util/Util_Bundle.res.mjs +64 -13
  135. package/src/util/Util_CognitoGroupUser_Runtime.res +32 -34
  136. package/src/util/Util_Cognito_Runtime.res +11 -7
  137. package/src/util/Util_DeadLetterQueue.res +12 -2
  138. package/src/util/Util_DynamoDb.res +5 -3
  139. package/src/util/Util_DynamoDbStream.res +7 -3
  140. package/src/util/Util_DynamoDbStream_Runtime.res +1 -2
  141. package/src/util/Util_DynamoDb_Runtime.res +23 -41
  142. package/src/util/Util_HostUiDomain.res +6 -1
  143. package/src/util/Util_Kinesis_Runtime.res +10 -11
  144. package/src/util/Util_Lambda.res +12 -5
  145. package/src/util/Util_LambdaEnvBudget.res +5 -5
  146. package/src/util/Util_LambdaLogging.res +1 -2
  147. package/src/util/Util_LogGroup_Adopting.res +1 -4
  148. package/src/util/Util_ResourceNaming.res +1 -2
  149. package/src/util/Util_SNS.res +8 -3
  150. package/src/util/Util_SNS_FIFO.res +1 -1
  151. package/src/util/Util_SNS_Runtime.res +11 -10
  152. package/src/util/Util_SQS.res +4 -2
  153. package/src/util/Util_SQS_FIFO.res +5 -2
  154. package/src/util/Util_SQS_Runtime.res +37 -36
  155. package/src/util/Util_ShellConfig.res +3 -2
  156. package/src/util/Util_StaticBundle.res +1 -3
  157. package/src/util/Util_StoreLayout.res +4 -1
  158. package/tests/AWS_TagsTest.res +16 -5
  159. package/tests/AppSyncEventsSigner_OpsTest.res +11 -2
  160. package/tests/AppSync_AdapterTest.res +203 -224
  161. package/tests/AppSync_Resolver_RetryingTest.res +64 -44
  162. package/tests/AppSync_RetirementNarrowingTest.res +59 -46
  163. package/tests/AppSync_SdlDecorateTest.res +22 -20
  164. package/tests/AppSync_SourceApiAssociation_RetryingTest.res +40 -22
  165. package/tests/Auth_ActiveRolePoolAttachmentTest.res +114 -107
  166. package/tests/Auth_ActiveRoleStoreTest.res +12 -9
  167. package/tests/Auth_ActiveRoleStore_SchemaTest.res +2 -1
  168. package/tests/Auth_ActiveRoleTrigger_OpsTest.res +20 -16
  169. package/tests/Auth_CognitoTest.res +31 -46
  170. package/tests/AutomationSliceEntryPoint_OpsTest.res +69 -67
  171. package/tests/BakeConvergenceTest.res +22 -25
  172. package/tests/Capability_MessagingTest.res +28 -12
  173. package/tests/CommandTopicQueuePolicyTest.res +1 -2
  174. package/tests/CounterEntryPoint_OpsTest.res +3 -7
  175. package/tests/DcbEventLogStorage_DynamoDb_RuntimeTest.res +306 -210
  176. package/tests/EventCollectorConnectLambdaPreviewTest.res +14 -7
  177. package/tests/EventCollectorEntryPoint_OpsTest.res +12 -4
  178. package/tests/EventCollectorPluginDefinitionAssetTest.res +18 -11
  179. package/tests/EventCollectorReadyGateTest.res +5 -4
  180. package/tests/EventLogStorage_DynamoDb_RuntimeTest.res +1 -2
  181. package/tests/EventMapperEntryPoint_OpsTest.res +1 -3
  182. package/tests/ExtensionPointEntryPoint_OpsTest.res +1 -4
  183. package/tests/HandlerFactoryDispatchTest.res +97 -70
  184. package/tests/MCP_LambdaTest.res +16 -27
  185. package/tests/PgChangeFeedRelay_IntegrationTest.res +123 -93
  186. package/tests/PgChangeFeedRelay_RuntimeTest.res +13 -10
  187. package/tests/PgMigrationEntryPoint_IntegrationTest.res +0 -1
  188. package/tests/PgPipeline_IntegrationTest.res +80 -66
  189. package/tests/PgQueryDbUnion_IntegrationTest.res +1 -2
  190. package/tests/PgQueryResolverEntryPoint_OpsTest.res +2 -6
  191. package/tests/PgQueryResolver_LambdaTest.res +41 -24
  192. package/tests/Platform_AdminScan_OpsTest.res +4 -1
  193. package/tests/Platform_ComponentDefinitions_Lambda_OpsTest.res +137 -129
  194. package/tests/ProjectionEntryPoint_DisplayNameTest.res +7 -3
  195. package/tests/ProjectionEntryPoint_UnionStampTest.res +3 -2
  196. package/tests/ProvisionIdentityTest.res +2 -6
  197. package/tests/QueryDbOwnerIndexTableTest.res +6 -4
  198. package/tests/QueryDbResolvers_AppSyncTest.res +23 -16
  199. package/tests/QueryInterceptor_LambdaTest.res +11 -13
  200. package/tests/ReadModelEntryPoint_OpsTest.res +43 -41
  201. package/tests/RuntimeExtensionEntryPoint_OpsTest.res +4 -3
  202. package/tests/SideEffectEntryPoint_OpsTest.res +23 -18
  203. package/tests/StateChangeDescriptorParityTest.res +28 -26
  204. package/tests/StateTopicKeySchemaTest.res +18 -15
  205. package/tests/StateViewSliceEntryPoint_OpsTest.res +55 -44
  206. package/tests/TaskBucketEntryPoint_OpsTest.res +13 -13
  207. package/tests/Upload_ClaimTest.res +3 -3
  208. package/tests/Util_AwsErrorTest.res +4 -7
  209. package/tests/Util_BundleDependencyClosureTest.res +41 -3
  210. package/tests/Util_BundleDependencyClosureTest.res.mjs +11 -0
  211. package/tests/Util_BundleEsmLoaderTest.res +3 -3
  212. package/tests/Util_BundleResolveTest.res +4 -6
  213. package/tests/Util_BundleRuntimeExtensionTest.res +17 -18
  214. package/tests/Util_DcbMetricsTest.res +6 -6
  215. package/tests/Util_DynamoDbStreamTest.res +4 -5
  216. package/tests/Util_DynamoDbTest.res +3 -2
  217. package/tests/Util_LambdaEnvBudgetTest.res +22 -18
  218. package/tests/Util_LambdaLoggingTest.res +9 -8
  219. package/tests/Util_LogGroup_AdoptingTest.res +10 -5
  220. package/tests/Util_LogRetentionTest.res +6 -2
  221. package/tests/Util_OwnerScopeEnvTest.res +10 -8
  222. package/tests/Util_SQS_RuntimeTest.res +5 -7
  223. package/tests/Util_ShellConfigTest.res +7 -17
  224. package/tests/Util_StaticBundleTest.res +3 -8
  225. package/tests/Util_StoreLayoutTest.res +17 -12
  226. package/tests/integration/AggregateEntryPoint_IntegrationTest.res +1 -4
  227. package/tests/integration/DcbCommandTopicEntryPoint_IntegrationTest.res +15 -15
  228. package/tests/integration/DcbEventLogStorage_DynamoDb_IntegrationTest.res +296 -232
  229. package/tests/integration/DcbIntegrationHarness.res +8 -8
  230. package/tests/integration/EpCompositeSlice.res +16 -8
  231. package/tests/integration/QueryDbUnion_DynamoDb_IntegrationTest.res +8 -10
  232. package/tests/integration/StateViewSliceEntryPoint_IntegrationTest.res +10 -10
  233. package/tests/integration/SvsTestSlice_Projection.res +1 -3
  234. package/tests/util/Util_AlarmSpecTest.res +62 -49
@@ -51,10 +51,7 @@ let describedPool = () =>
51
51
  ),
52
52
  ])->JSON.Encode.object,
53
53
  ),
54
- (
55
- "UserPoolTags",
56
- Dict.fromArray([("CostCentre", str("identity"))])->JSON.Encode.object,
57
- ),
54
+ ("UserPoolTags", Dict.fromArray([("CostCentre", str("identity"))])->JSON.Encode.object),
58
55
  (
59
56
  "LambdaConfig",
60
57
  Dict.fromArray([
@@ -73,8 +70,7 @@ let merged = (~preTokenGenerationArn) =>
73
70
 
74
71
  let triggerArn = "arn:aws:lambda:eu-west-1:1:function:ActiveRoleTrigger"
75
72
 
76
- let lambdaConfigOf = input =>
77
- input->Dict.get("LambdaConfig")->Option.flatMap(JSON.Decode.object)
73
+ let lambdaConfigOf = input => input->Dict.get("LambdaConfig")->Option.flatMap(JSON.Decode.object)
78
74
 
79
75
  /** The `PreTokenGenerationConfig` an attach is expected to write: our ARN, at the
80
76
  only version the trigger handler implements. */
@@ -159,10 +155,16 @@ describe("Auth_ActiveRolePoolAttachment.mergedUpdateInput — shaping for the AP
159
155
 
160
156
  testSync("read-only fields UpdateUserPool rejects are not sent", () =>
161
157
  expect(
162
- ["Id", "Arn", "Status", "CreationDate", "LastModifiedDate", "SchemaAttributes",
163
- "UsernameAttributes", "EstimatedNumberOfUsers"]->Array.filter(k =>
164
- input->Dict.get(k)->Option.isSome
165
- ),
158
+ [
159
+ "Id",
160
+ "Arn",
161
+ "Status",
162
+ "CreationDate",
163
+ "LastModifiedDate",
164
+ "SchemaAttributes",
165
+ "UsernameAttributes",
166
+ "EstimatedNumberOfUsers",
167
+ ]->Array.filter(k => input->Dict.get(k)->Option.isSome),
166
168
  )->toEqual([])
167
169
  )
168
170
  })
@@ -211,98 +213,102 @@ describe("Auth_ActiveRolePoolAttachment.mergedUpdateInput — a pool with nothin
211
213
  // in two fields, and `UpdateUserPool` rejects the call when both are present
212
214
  // naming different functions. Nothing in this suite carried a
213
215
  // `PreTokenGenerationConfig` before, which is exactly why it shipped.
214
- describe("Auth_ActiveRolePoolAttachment.mergedUpdateInput — a pool already on the V2 field", () => {
215
- let theirArn = "arn:aws:lambda:eu-west-1:1:function:TheirOwnPreToken"
216
-
217
- let describedWithConfig = (~version) =>
218
- Dict.fromArray([
219
- ("Name", str("CustomerPool")),
220
- (
221
- "LambdaConfig",
222
- Dict.fromArray([
223
- ("PreSignUp", str("arn:aws:lambda:eu-west-1:1:function:TheirPreSignUp")),
224
- ("PreTokenGeneration", str(theirArn)),
225
- (
226
- "PreTokenGenerationConfig",
227
- Dict.fromArray([
228
- ("LambdaArn", str(theirArn)),
229
- ("LambdaVersion", str(version)),
230
- ])->JSON.Encode.object,
231
- ),
232
- ])->JSON.Encode.object,
233
- ),
234
- ])
235
-
236
- let attachOnto = (~version) =>
237
- Attachment.mergedUpdateInput(
238
- ~described=describedWithConfig(~version),
239
- ~userPoolId="eu-west-1_abc123",
240
- ~preTokenGenerationArn=Some(triggerArn),
241
- )
242
-
243
- // The property AWS enforces, asserted directly rather than through its error
244
- // text: the two fields must name the same function.
245
- testSync("both fields name our trigger, and the same one", () => {
246
- let config = lambdaConfigOf(attachOnto(~version="V1_0"))
247
- expect((
248
- config->Option.flatMap(c => c->Dict.get("PreTokenGeneration")),
249
- config
250
- ->Option.flatMap(c => c->Dict.get("PreTokenGenerationConfig"))
251
- ->Option.flatMap(JSON.Decode.object)
252
- ->Option.flatMap(c => c->Dict.get("LambdaArn")),
253
- ))->toEqual((Some(str(triggerArn)), Some(str(triggerArn))))
254
- })
216
+ describe(
217
+ "Auth_ActiveRolePoolAttachment.mergedUpdateInput — a pool already on the V2 field",
218
+ () => {
219
+ let theirArn = "arn:aws:lambda:eu-west-1:1:function:TheirOwnPreToken"
255
220
 
256
- // A pool left on V2_0 does not fail — the handler answers in V1_0 shape and
257
- // Cognito ignores it, so tokens mint un-narrowed and nothing reports it.
258
- testSync("a pool that arrived on V2_0 is pinned back to the version we implement", () =>
259
- expect(
260
- lambdaConfigOf(attachOnto(~version="V2_0"))
261
- ->Option.flatMap(c => c->Dict.get("PreTokenGenerationConfig"))
262
- ->Option.flatMap(JSON.Decode.object)
263
- ->Option.flatMap(c => c->Dict.get("LambdaVersion")),
264
- )->toEqual(Some(str("V1_0")))
265
- )
266
-
267
- testSync("the customer's other triggers still survive", () =>
268
- expect(
269
- lambdaConfigOf(attachOnto(~version="V1_0"))->Option.flatMap(c => c->Dict.get("PreSignUp")),
270
- )->toEqual(Some(str("arn:aws:lambda:eu-west-1:1:function:TheirPreSignUp")))
271
- )
221
+ let describedWithConfig = (~version) =>
222
+ Dict.fromArray([
223
+ ("Name", str("CustomerPool")),
224
+ (
225
+ "LambdaConfig",
226
+ Dict.fromArray([
227
+ ("PreSignUp", str("arn:aws:lambda:eu-west-1:1:function:TheirPreSignUp")),
228
+ ("PreTokenGeneration", str(theirArn)),
229
+ (
230
+ "PreTokenGenerationConfig",
231
+ Dict.fromArray([
232
+ ("LambdaArn", str(theirArn)),
233
+ ("LambdaVersion", str(version)),
234
+ ])->JSON.Encode.object,
235
+ ),
236
+ ])->JSON.Encode.object,
237
+ ),
238
+ ])
272
239
 
273
- testSync("detaching removes both fields, not just the legacy one", () => {
274
- let config = lambdaConfigOf(
240
+ let attachOnto = (~version) =>
275
241
  Attachment.mergedUpdateInput(
276
- ~described=describedWithConfig(~version="V1_0"),
242
+ ~described=describedWithConfig(~version),
277
243
  ~userPoolId="eu-west-1_abc123",
278
- ~preTokenGenerationArn=None,
279
- ),
244
+ ~preTokenGenerationArn=Some(triggerArn),
245
+ )
246
+
247
+ // The property AWS enforces, asserted directly rather than through its error
248
+ // text: the two fields must name the same function.
249
+ testSync("both fields name our trigger, and the same one", () => {
250
+ let config = lambdaConfigOf(attachOnto(~version="V1_0"))
251
+ expect((
252
+ config->Option.flatMap(c => c->Dict.get("PreTokenGeneration")),
253
+ config
254
+ ->Option.flatMap(c => c->Dict.get("PreTokenGenerationConfig"))
255
+ ->Option.flatMap(JSON.Decode.object)
256
+ ->Option.flatMap(c => c->Dict.get("LambdaArn")),
257
+ ))->toEqual((Some(str(triggerArn)), Some(str(triggerArn))))
258
+ })
259
+
260
+ // A pool left on V2_0 does not fail — the handler answers in V1_0 shape and
261
+ // Cognito ignores it, so tokens mint un-narrowed and nothing reports it.
262
+ testSync("a pool that arrived on V2_0 is pinned back to the version we implement", () =>
263
+ expect(
264
+ lambdaConfigOf(attachOnto(~version="V2_0"))
265
+ ->Option.flatMap(c => c->Dict.get("PreTokenGenerationConfig"))
266
+ ->Option.flatMap(JSON.Decode.object)
267
+ ->Option.flatMap(c => c->Dict.get("LambdaVersion")),
268
+ )->toEqual(Some(str("V1_0")))
280
269
  )
281
- expect((
282
- config->Option.flatMap(c => c->Dict.get("PreTokenGeneration")),
283
- config->Option.flatMap(c => c->Dict.get("PreTokenGenerationConfig")),
284
- ))->toEqual((None, None))
285
- })
286
270
 
287
- // The fixpoint property, and the one that would have caught this: AWS
288
- // materialises whichever field the merge did not write, so the merge has to
289
- // accept its own output unchanged or a later deploy fails with nothing altered
290
- // but the service normalising its own record.
291
- testSync("feeding the merge its own output back changes nothing", () => {
292
- let once = attachOnto(~version="V1_0")
293
- let twice = Attachment.mergedUpdateInput(
294
- ~described=once,
295
- ~userPoolId="eu-west-1_abc123",
296
- ~preTokenGenerationArn=Some(triggerArn),
271
+ testSync("the customer's other triggers still survive", () =>
272
+ expect(
273
+ lambdaConfigOf(attachOnto(~version="V1_0"))->Option.flatMap(c => c->Dict.get("PreSignUp")),
274
+ )->toEqual(Some(str("arn:aws:lambda:eu-west-1:1:function:TheirPreSignUp")))
297
275
  )
298
- expect(lambdaConfigOf(twice))->toEqual(lambdaConfigOf(once))
299
- })
300
- })
276
+
277
+ testSync("detaching removes both fields, not just the legacy one", () => {
278
+ let config = lambdaConfigOf(
279
+ Attachment.mergedUpdateInput(
280
+ ~described=describedWithConfig(~version="V1_0"),
281
+ ~userPoolId="eu-west-1_abc123",
282
+ ~preTokenGenerationArn=None,
283
+ ),
284
+ )
285
+ expect((
286
+ config->Option.flatMap(c => c->Dict.get("PreTokenGeneration")),
287
+ config->Option.flatMap(c => c->Dict.get("PreTokenGenerationConfig")),
288
+ ))->toEqual((None, None))
289
+ })
290
+
291
+ // The fixpoint property, and the one that would have caught this: AWS
292
+ // materialises whichever field the merge did not write, so the merge has to
293
+ // accept its own output unchanged or a later deploy fails with nothing altered
294
+ // but the service normalising its own record.
295
+ testSync("feeding the merge its own output back changes nothing", () => {
296
+ let once = attachOnto(~version="V1_0")
297
+ let twice = Attachment.mergedUpdateInput(
298
+ ~described=once,
299
+ ~userPoolId="eu-west-1_abc123",
300
+ ~preTokenGenerationArn=Some(triggerArn),
301
+ )
302
+ expect(lambdaConfigOf(twice))->toEqual(lambdaConfigOf(once))
303
+ })
304
+ },
305
+ )
301
306
 
302
307
  describe("Auth_ActiveRolePoolAttachment.attachedTrigger", () => {
303
308
  testSync("reports the trigger a described pool carries", () =>
304
- expect(Attachment.attachedTrigger(~described=merged(~preTokenGenerationArn=Some(triggerArn))))
305
- ->toEqual(Some(triggerArn))
309
+ expect(
310
+ Attachment.attachedTrigger(~described=merged(~preTokenGenerationArn=Some(triggerArn))),
311
+ )->toEqual(Some(triggerArn))
306
312
  )
307
313
 
308
314
  testSync("reports none when the pool carries no trigger", () =>
@@ -317,7 +323,9 @@ describe("Auth_ActiveRolePoolAttachment.attachedTrigger", () => {
317
323
  ~described=Dict.fromArray([
318
324
  (
319
325
  "LambdaConfig",
320
- Dict.fromArray([("PreTokenGenerationConfig", v1Config(triggerArn))])->JSON.Encode.object,
326
+ Dict.fromArray([
327
+ ("PreTokenGenerationConfig", v1Config(triggerArn)),
328
+ ])->JSON.Encode.object,
321
329
  ),
322
330
  ]),
323
331
  ),
@@ -439,9 +447,7 @@ describe("Auth_ActiveRolePoolAttachment.probeEvent", () => {
439
447
 
440
448
  // Both halves of the key must miss every real row, not just the subject.
441
449
  testSync("the probe's subject and client cannot collide with a real row", () =>
442
- expect(
443
- Attachment.probeSubject == Attachment.probeClientId,
444
- )->toBe(false)
450
+ expect(Attachment.probeSubject == Attachment.probeClientId)->toBe(false)
445
451
  )
446
452
  })
447
453
 
@@ -470,8 +476,9 @@ describe("Auth_ActiveRolePoolAttachment.attachedTriggerArn", () => {
470
476
  ])
471
477
 
472
478
  testSync("an empty pool holds nothing", () =>
473
- expect(Attachment.attachedTriggerArn(~described=Dict.fromArray([("Name", str("Bare"))])))
474
- ->toEqual(None)
479
+ expect(
480
+ Attachment.attachedTriggerArn(~described=Dict.fromArray([("Name", str("Bare"))])),
481
+ )->toEqual(None)
475
482
  )
476
483
 
477
484
  testSync("reports the trigger a pool carries", () =>
@@ -539,8 +546,9 @@ describe("Auth_ActiveRolePoolAttachment.classifySlot", () => {
539
546
  // halves. Unreachable by configuration now that the store is derived — reachable
540
547
  // by version skew, while an older release is still on its stack-scoped table.
541
548
  testSync("another deployment's trigger on a different store is the defect", () =>
542
- expect(classify(~attachedArn=Some(theirs), ~attachedStore=Some("ActiveRoleStore-829c96f")))
543
- ->toEqual(Attachment.DifferentStore({arn: theirs, theirStore: "ActiveRoleStore-829c96f"}))
549
+ expect(
550
+ classify(~attachedArn=Some(theirs), ~attachedStore=Some("ActiveRoleStore-829c96f")),
551
+ )->toEqual(Attachment.DifferentStore({arn: theirs, theirStore: "ActiveRoleStore-829c96f"}))
544
552
  )
545
553
 
546
554
  // Reading the store rather than matching a name is what makes this a check on
@@ -568,13 +576,12 @@ describe("Auth_ActiveRolePoolAttachment.refusalFor", () => {
568
576
  // Both stores named, because an operator cannot act on this without knowing
569
577
  // which two are in disagreement.
570
578
  testSync("a disagreeing store is refused, naming both stores", () => {
571
- let message =
572
- refusal(
573
- Attachment.DifferentStore({
574
- arn: "arn:aws:lambda:eu-west-1:1:function:Other",
575
- theirStore: "ActiveRoleStore-829c96f",
576
- }),
577
- )->Option.getOr("")
579
+ let message = refusal(
580
+ Attachment.DifferentStore({
581
+ arn: "arn:aws:lambda:eu-west-1:1:function:Other",
582
+ theirStore: "ActiveRoleStore-829c96f",
583
+ }),
584
+ )->Option.getOr("")
578
585
  expect((
579
586
  message->String.includes(store),
580
587
  message->String.includes("ActiveRoleStore-829c96f"),
@@ -18,8 +18,9 @@ describe("Auth_ActiveRoleStore_Ops.mayActAs — the conformance table", () => {
18
18
  // path, exercised against the handler rather than the predicate.
19
19
  | None => ()
20
20
  | Some(role) =>
21
- testSync(label, () =>
22
- expect(Ops.mayActAs(~membership, ~requested=role))->toBe(expected->Option.isSome)
21
+ testSync(
22
+ label,
23
+ () => expect(Ops.mayActAs(~membership, ~requested=role))->toBe(expected->Option.isSome),
23
24
  )
24
25
  }
25
26
  )
@@ -48,7 +49,9 @@ describe("Auth_ActiveRoleStore_Ops.mayActAs — narrowing only", () => {
48
49
  describe("Auth_ActiveRoleStore_Ops.cognitoLookupName", () => {
49
50
  testSync("the username is what addresses Cognito when the authorizer sent one", () =>
50
51
  expect(
51
- Ops.cognitoLookupName(~identity={sub: "d265d464-2091-706a-e5f9-3afafe7be29c", username: Value("merch")}),
52
+ Ops.cognitoLookupName(
53
+ ~identity={sub: "d265d464-2091-706a-e5f9-3afafe7be29c", username: Value("merch")},
54
+ ),
52
55
  )->toEqual(Some("merch"))
53
56
  )
54
57
 
@@ -85,10 +88,9 @@ describe("Auth_ActiveRoleStore.chooseStore", () => {
85
88
  )
86
89
 
87
90
  testSync("a stack given a provider reads that provider's derived store", () =>
88
- expect(Auth_ActiveRoleStore.chooseStore(~identityProviderId=Some("eu-west-1_CQTwafSeX")))
89
- ->toEqual(
90
- Auth_ActiveRoleStore.ProviderScoped("ReventlessActiveRoleStore-eu-west-1_CQTwafSeX"),
91
- )
91
+ expect(
92
+ Auth_ActiveRoleStore.chooseStore(~identityProviderId=Some("eu-west-1_CQTwafSeX")),
93
+ )->toEqual(Auth_ActiveRoleStore.ProviderScoped("ReventlessActiveRoleStore-eu-west-1_CQTwafSeX"))
92
94
  )
93
95
 
94
96
  // 🚨 The defect, expressed as the property that now prevents it. Two platform
@@ -97,8 +99,9 @@ describe("Auth_ActiveRoleStore.chooseStore", () => {
97
99
  // doing nothing. There is no configuration either could carry that would make
98
100
  // them disagree.
99
101
  testSync("two deployments on one provider cannot choose different stores", () =>
100
- expect(Auth_ActiveRoleStore.chooseStore(~identityProviderId=Some("eu-west-1_Shared")))
101
- ->toEqual(Auth_ActiveRoleStore.chooseStore(~identityProviderId=Some("eu-west-1_Shared")))
102
+ expect(Auth_ActiveRoleStore.chooseStore(~identityProviderId=Some("eu-west-1_Shared")))->toEqual(
103
+ Auth_ActiveRoleStore.chooseStore(~identityProviderId=Some("eu-west-1_Shared")),
104
+ )
102
105
  )
103
106
  })
104
107
 
@@ -73,7 +73,8 @@ describe("Auth_ActiveRoleStore_Schema.keySchemaRefusal", () => {
73
73
  )
74
74
 
75
75
  testSync("the refusal names the table, what it found, and what is needed", () => {
76
- let message = Schema.keySchemaRefusal(~tableName=store, ~actual=[("id", "HASH")])->Option.getOr("")
76
+ let message =
77
+ Schema.keySchemaRefusal(~tableName=store, ~actual=[("id", "HASH")])->Option.getOr("")
77
78
  expect((
78
79
  message->String.includes(store),
79
80
  message->String.includes("id:HASH"),
@@ -18,14 +18,13 @@ let eventFor = (
18
18
  groupConfiguration: {
19
19
  groupsToOverride: membership,
20
20
  iamRolesToOverride: iamRoles,
21
- preferredRole: ?preferredRole->Option.map(Nullable.make),
21
+ preferredRole: ?(preferredRole->Option.map(Nullable.make)),
22
22
  },
23
23
  },
24
24
  userName: "carol",
25
25
  }
26
26
 
27
- let overrideOf = (e: Trigger.event) =>
28
- e.response->Option.flatMap(r => r.claimsOverrideDetails)
27
+ let overrideOf = (e: Trigger.event) => e.response->Option.flatMap(r => r.claimsOverrideDetails)
29
28
 
30
29
  let groupsOf = (e: Trigger.event) =>
31
30
  overrideOf(e)->Option.flatMap(c => c.groupOverrideDetails)->Option.map(g => g.groupsToOverride)
@@ -51,18 +50,20 @@ let claimOf = (e: Trigger.event, key) =>
51
50
  // the same token. That an absent override really is absent (rather than an empty
52
51
  // one, which is not the same thing) is asserted separately below.
53
52
 
54
- let effectiveGroups = (e: Trigger.event, ~membership) =>
55
- groupsOf(e)->Option.getOr(membership)
53
+ let effectiveGroups = (e: Trigger.event, ~membership) => groupsOf(e)->Option.getOr(membership)
56
54
 
57
55
  describe("the conformance table, minted by the trigger", () => {
58
56
  Contract.conformanceCases->Array.forEach(({label, membership, requested, expected}) =>
59
- testSync(label, () => {
60
- let event = Trigger.respond(
61
- ~event=eventFor(~membership),
62
- ~decision=Trigger.decide(~membership, ~storedRole=requested),
63
- )
64
- expect(effectiveGroups(event, ~membership))->toEqual(expected->Option.getOr(membership))
65
- })
57
+ testSync(
58
+ label,
59
+ () => {
60
+ let event = Trigger.respond(
61
+ ~event=eventFor(~membership),
62
+ ~decision=Trigger.decide(~membership, ~storedRole=requested),
63
+ )
64
+ expect(effectiveGroups(event, ~membership))->toEqual(expected->Option.getOr(membership))
65
+ },
66
+ )
66
67
  )
67
68
  })
68
69
 
@@ -76,9 +77,9 @@ describe("Auth_ActiveRoleTrigger_Ops.decide", () => {
76
77
  )
77
78
 
78
79
  testSync("a held role narrows", () =>
79
- expect(
80
- Trigger.decide(~membership=["Admin", "Shopper"], ~storedRole=Some("Shopper")),
81
- )->toEqual(Trigger.Narrow({role: "Shopper", membership: ["Admin", "Shopper"]}))
80
+ expect(Trigger.decide(~membership=["Admin", "Shopper"], ~storedRole=Some("Shopper")))->toEqual(
81
+ Trigger.Narrow({role: "Shopper", membership: ["Admin", "Shopper"]}),
82
+ )
82
83
  )
83
84
 
84
85
  // The case the local path never had to answer: the row outlives the membership.
@@ -152,7 +153,10 @@ describe("Auth_ActiveRoleTrigger_Ops.respond", () => {
152
153
  expect((
153
154
  details->Option.map(d => d.iamRolesToOverride),
154
155
  details->Option.flatMap(d => d.preferredRole),
155
- ))->toEqual((Some(["arn:aws:iam::1:role/Shopper"]), Some(Nullable.make("arn:aws:iam::1:role/Shopper"))))
156
+ ))->toEqual((
157
+ Some(["arn:aws:iam::1:role/Shopper"]),
158
+ Some(Nullable.make("arn:aws:iam::1:role/Shopper")),
159
+ ))
156
160
  })
157
161
 
158
162
  testSync("a stale mint carries them through too", () => {
@@ -41,9 +41,7 @@ describe("Auth_Cognito.authenticate", () => {
41
41
  })
42
42
 
43
43
  testAsync("non-Bearer Authorization header → Anonymous", async () => {
44
- let r = await Auth_Cognito.authenticate(
45
- buildContext([("authorization", "Basic dXNlcjpwYXNz")]),
46
- )
44
+ let r = await Auth_Cognito.authenticate(buildContext([("authorization", "Basic dXNlcjpwYXNz")]))
47
45
  switch r {
48
46
  | Anonymous => ()
49
47
  | _ => JsError.throwWithMessage("expected Anonymous")
@@ -51,9 +49,7 @@ describe("Auth_Cognito.authenticate", () => {
51
49
  })
52
50
 
53
51
  testAsync("malformed Bearer token → AuthError", async () => {
54
- let r = await Auth_Cognito.authenticate(
55
- buildContext([("authorization", "Bearer not-a-jwt")]),
56
- )
52
+ let r = await Auth_Cognito.authenticate(buildContext([("authorization", "Bearer not-a-jwt")]))
57
53
  switch r {
58
54
  | AuthError(_) => ()
59
55
  | _ => JsError.throwWithMessage("expected AuthError")
@@ -64,15 +60,10 @@ describe("Auth_Cognito.authenticate", () => {
64
60
  let claims = Dict.fromArray([
65
61
  ("sub", JSON.Encode.string("c3741234-aaaa-bbbb-cccc-ddddeeee1234")),
66
62
  ("cognito:username", JSON.Encode.string("alice@example.com")),
67
- (
68
- "cognito:groups",
69
- ["Admin", "User"]->Array.map(JSON.Encode.string)->JSON.Encode.array,
70
- ),
63
+ ("cognito:groups", ["Admin", "User"]->Array.map(JSON.Encode.string)->JSON.Encode.array),
71
64
  ])
72
65
  let token = buildJwt(claims)
73
- let r = await Auth_Cognito.authenticate(
74
- buildContext([("authorization", "Bearer " ++ token)]),
75
- )
66
+ let r = await Auth_Cognito.authenticate(buildContext([("authorization", "Bearer " ++ token)]))
76
67
  switch r {
77
68
  | Authenticated(identity) =>
78
69
  expect(identity.userId)->toBe("c3741234-aaaa-bbbb-cccc-ddddeeee1234")
@@ -88,9 +79,7 @@ describe("Auth_Cognito.authenticate", () => {
88
79
  ("cognito:username", JSON.Encode.string("bob")),
89
80
  ])
90
81
  let token = buildJwt(claims)
91
- let r = await Auth_Cognito.authenticate(
92
- buildContext([("authorization", "Bearer " ++ token)]),
93
- )
82
+ let r = await Auth_Cognito.authenticate(buildContext([("authorization", "Bearer " ++ token)]))
94
83
  switch r {
95
84
  | Authenticated(identity) =>
96
85
  expect(identity.groups)->toEqual([])
@@ -100,13 +89,9 @@ describe("Auth_Cognito.authenticate", () => {
100
89
  })
101
90
 
102
91
  testAsync("header lookup is case-insensitive (Authorization)", async () => {
103
- let claims = Dict.fromArray([
104
- ("sub", JSON.Encode.string("user-mixed-case")),
105
- ])
92
+ let claims = Dict.fromArray([("sub", JSON.Encode.string("user-mixed-case"))])
106
93
  let token = buildJwt(claims)
107
- let r = await Auth_Cognito.authenticate(
108
- buildContext([("Authorization", "Bearer " ++ token)]),
109
- )
94
+ let r = await Auth_Cognito.authenticate(buildContext([("Authorization", "Bearer " ++ token)]))
110
95
  switch r {
111
96
  | Authenticated(identity) => expect(identity.userId)->toBe("user-mixed-case")
112
97
  | _ => JsError.throwWithMessage("expected Authenticated")
@@ -124,31 +109,31 @@ describe("Auth_Cognito.fromAppSyncIdentity", () => {
124
109
  }
125
110
  })
126
111
 
127
- testSync("Cognito identity: sub + claims.cognito:groups → Authenticated with Cognito provider", () => {
128
- let claims = Dict.fromArray([
129
- ("cognito:username", JSON.Encode.string("alice@example.com")),
130
- (
131
- "cognito:groups",
132
- ["Admin"]->Array.map(JSON.Encode.string)->JSON.Encode.array,
133
- ),
134
- ])
135
- let id: Auth_Cognito.appSyncIdentity = {
136
- sub: "c3741234-aaaa",
137
- username: "alice@example.com",
138
- claims,
139
- }
140
- switch Auth_Cognito.fromAppSyncIdentity(Some(id)) {
141
- | Authenticated(identity) =>
142
- expect(identity.userId)->toBe("c3741234-aaaa")
143
- expect(identity.username)->toBe("alice@example.com")
144
- expect(identity.groups)->toEqual(["Admin"])
145
- switch identity.provider {
146
- | Cognito => ()
147
- | _ => JsError.throwWithMessage("expected Cognito provider")
112
+ testSync(
113
+ "Cognito identity: sub + claims.cognito:groups → Authenticated with Cognito provider",
114
+ () => {
115
+ let claims = Dict.fromArray([
116
+ ("cognito:username", JSON.Encode.string("alice@example.com")),
117
+ ("cognito:groups", ["Admin"]->Array.map(JSON.Encode.string)->JSON.Encode.array),
118
+ ])
119
+ let id: Auth_Cognito.appSyncIdentity = {
120
+ sub: "c3741234-aaaa",
121
+ username: "alice@example.com",
122
+ claims,
148
123
  }
149
- | _ => JsError.throwWithMessage("expected Authenticated")
150
- }
151
- })
124
+ switch Auth_Cognito.fromAppSyncIdentity(Some(id)) {
125
+ | Authenticated(identity) =>
126
+ expect(identity.userId)->toBe("c3741234-aaaa")
127
+ expect(identity.username)->toBe("alice@example.com")
128
+ expect(identity.groups)->toEqual(["Admin"])
129
+ switch identity.provider {
130
+ | Cognito => ()
131
+ | _ => JsError.throwWithMessage("expected Cognito provider")
132
+ }
133
+ | _ => JsError.throwWithMessage("expected Authenticated")
134
+ }
135
+ },
136
+ )
152
137
 
153
138
  testSync("Cognito identity: missing claims.cognito:groups → empty groups", () => {
154
139
  let id: Auth_Cognito.appSyncIdentity = {