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

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 (263) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/package.json +15 -12
  3. package/run-provision-accounts.mjs +3 -0
  4. package/run-provision-admin.mjs +3 -0
  5. package/scripts/ProvisionAccounts.res +325 -0
  6. package/scripts/ProvisionAccounts.res.mjs +337 -0
  7. package/scripts/ProvisionAdmin.res +275 -0
  8. package/scripts/ProvisionAdmin.res.mjs +289 -0
  9. package/scripts/ProvisionCognito.res +149 -0
  10. package/scripts/ProvisionCognito.res.mjs +98 -0
  11. package/scripts/ProvisionIdentity.res +84 -14
  12. package/scripts/ProvisionIdentity.res.mjs +72 -7
  13. package/scripts/ProvisionProvider.res +104 -0
  14. package/scripts/ProvisionProvider.res.mjs +123 -0
  15. package/src/Platform.res +358 -329
  16. package/src/Platform.res.mjs +24 -12
  17. package/src/Platform_Casts.res +13 -4
  18. package/src/Platform_Stack.res +119 -9
  19. package/src/Platform_Stack.res.mjs +42 -4
  20. package/src/adapter/Api/AppSyncEventsSigner_Ops.res +8 -4
  21. package/src/adapter/Api/AppSync_EventsApi.res +12 -14
  22. package/src/adapter/Api/AppSync_Resolver_Native.res +3 -4
  23. package/src/adapter/Api/AppSync_Resolver_Retrying.res +78 -70
  24. package/src/adapter/Api/AppSync_SourceApiAssociation_Retrying.res +11 -8
  25. package/src/adapter/Api/Platform_AdminScan_Ops.res +15 -14
  26. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res +10 -2
  27. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res +40 -50
  28. package/src/adapter/Api/Platform_UIFragments_Lambda.res +39 -32
  29. package/src/adapter/Api/Platform_UIFragments_Lambda_Ops.res +5 -2
  30. package/src/adapter/Auth/Auth_ActiveRolePoolAttachment.res +0 -1
  31. package/src/adapter/Auth/Auth_ActiveRoleStore.res +1 -2
  32. package/src/adapter/Auth/Auth_ActiveRoleStore_Schema.res +1 -4
  33. package/src/adapter/Auth/Auth_ActiveRoleTrigger_Ops.res +5 -2
  34. package/src/adapter/Auth/Auth_Cognito.res +3 -9
  35. package/src/adapter/Auth/Auth_LoginIdentifier.res +66 -0
  36. package/src/adapter/Auth/Auth_LoginIdentifier.res.mjs +70 -0
  37. package/src/adapter/Auth/Auth_SignUpMode.res +78 -0
  38. package/src/adapter/Auth/Auth_SignUpMode.res.mjs +56 -0
  39. package/src/adapter/CommandGenerator/CommandGeneratorResolvers_AppSync.res +1 -4
  40. package/src/adapter/CommandGenerator/CommandGeneratorResolvers_AppSync_NoOp.res +3 -1
  41. package/src/adapter/CommandGenerator/InboundTranslationResolvers_AppSync.res +1 -4
  42. package/src/adapter/CommandTopic/CommandTopicChannel_SQS.res +7 -4
  43. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Async.res +6 -1
  44. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_FIFO.res +7 -4
  45. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Runtime.res +24 -18
  46. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Sync.res +9 -4
  47. package/src/adapter/CommandTopic/CommandTopicRegistry.res +1 -4
  48. package/src/adapter/Counter/CounterHandler_DynamoDbStream.res +15 -15
  49. package/src/adapter/DcbEventLog/DcbEventLogStorage_DynamoDb.res +9 -5
  50. package/src/adapter/DcbEventLog/DcbEventLogStorage_DynamoDb_Runtime.res +179 -213
  51. package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream.res +5 -5
  52. package/src/adapter/EventCollector/EventCollectorChannel_Helpers.res +6 -2
  53. package/src/adapter/EventCollector/EventCollectorChannel_SQS.res +14 -7
  54. package/src/adapter/EventCollector/EventCollectorChannel_SQS_FIFO.res +14 -7
  55. package/src/adapter/EventCollector/EventCollectorChannel_SQS_Runtime.res +6 -3
  56. package/src/adapter/EventLog/EventLogStorage_DynamoDbStream.res +6 -1
  57. package/src/adapter/EventLog/EventLogStorage_DynamoDb_Runtime.res +13 -17
  58. package/src/adapter/EventLog/EventLogStorage_Postgres.res +7 -4
  59. package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res +12 -7
  60. package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync_Ops.res +4 -2
  61. package/src/adapter/EventTopic/EventTopicPublisher_DynamoDbStream.res +3 -2
  62. package/src/adapter/EventTopic/EventTopicPublisher_SNS.res +20 -12
  63. package/src/adapter/EventTopic/EventTopicPublisher_SNS_FIFO.res +21 -9
  64. package/src/adapter/EventTopic/EventTopicPublisher_SNS_Runtime.res +5 -3
  65. package/src/adapter/Geocoder/Geocoder_AwsLocation_Resolver.res +6 -3
  66. package/src/adapter/Mcp/MCP_Lambda.res +31 -46
  67. package/src/adapter/Messaging/Messaging_Ses_Backend.res +11 -7
  68. package/src/adapter/Messaging/Messaging_Ses_Backend.res.mjs +2 -4
  69. package/src/adapter/Monitoring/Monitoring_CloudWatch.res +16 -16
  70. package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res +5 -10
  71. package/src/adapter/Postgres/PgChangeFeedRelay_Runtime.res +4 -2
  72. package/src/adapter/Postgres/PgConnection.res +6 -5
  73. package/src/adapter/Postgres/PgMigration_Builder.res +4 -8
  74. package/src/adapter/Postgres/PgRuntime.res +4 -7
  75. package/src/adapter/QueryDb/PgQueryResolver_Builder.res +9 -11
  76. package/src/adapter/QueryDb/PgQueryResolver_Lambda.res +22 -21
  77. package/src/adapter/QueryDb/QueryDbResolvers_AppSync.res +114 -111
  78. package/src/adapter/QueryDb/QueryDbResolvers_Lambda.res +1 -7
  79. package/src/adapter/QueryDb/QueryDbStorage.res +4 -2
  80. package/src/adapter/QueryDb/QueryDbStorage_DynamoDb.res +23 -24
  81. package/src/adapter/QueryDb/QueryDbStorage_DynamoDbStream.res +8 -2
  82. package/src/adapter/QueryDb/QueryDbStorage_DynamoDb_Runtime.res +71 -73
  83. package/src/adapter/QueryDb/QueryDbStorage_Postgres.res +5 -2
  84. package/src/adapter/QueryDb/QueryInterceptor_Provisioning.res +1 -4
  85. package/src/adapter/QueryEngine/QueryEngine_DynamoDb.res +8 -2
  86. package/src/adapter/Runtime/AggregateRuntime_Builder_Micro.res +73 -81
  87. package/src/adapter/Runtime/AggregateRuntime_Builder_Micro_Async.res +72 -78
  88. package/src/adapter/Runtime/AggregateRuntime_Builder_PerAggregate.res +44 -67
  89. package/src/adapter/Runtime/AggregateRuntime_Builder_PerAggregate_Async.res +44 -67
  90. package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res +53 -82
  91. package/src/adapter/Runtime/AggregateRuntime_Builder_Single_Async.res +47 -77
  92. package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res +23 -11
  93. package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res.mjs +5 -1
  94. package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res +80 -97
  95. package/src/adapter/Runtime/CounterEntryPoint_Ops.res +2 -7
  96. package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res +16 -10
  97. package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res +24 -15
  98. package/src/adapter/Runtime/EventCollectorRuntime_Builder_PerEventCollector.res +17 -37
  99. package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res +31 -57
  100. package/src/adapter/Runtime/EventMapperEntryPoint_Ops.res +18 -24
  101. package/src/adapter/Runtime/ExtensionPointEntryPoint_Ops.res +1 -5
  102. package/src/adapter/Runtime/ExtensionPointRuntime_Builder_PerExtensionPoint.res +4 -3
  103. package/src/adapter/Runtime/HeartbeatEntryPoint.res +0 -1
  104. package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.res +4 -2
  105. package/src/adapter/Runtime/PgMigrationEntryPoint.res +0 -1
  106. package/src/adapter/Runtime/PgQueryResolverEntryPoint_Ops.res +3 -2
  107. package/src/adapter/Runtime/PluginExtensionPointEntryPoint.res +1 -2
  108. package/src/adapter/Runtime/ProjectionEntryPoint_Ops.res +4 -5
  109. package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res +22 -10
  110. package/src/adapter/Runtime/RuntimeExtensionEntryPoint_Ops.res +8 -2
  111. package/src/adapter/Runtime/SideEffectHandlerRuntime_Builder_Single.res +14 -31
  112. package/src/adapter/Runtime/StateChangeSliceRuntime_Builder_Single.res +16 -17
  113. package/src/adapter/Runtime/StateViewSliceEntryPoint_Ops.res +10 -9
  114. package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res +46 -52
  115. package/src/adapter/Runtime/StreamRoutedEntryPoint_Ops.res +36 -41
  116. package/src/adapter/Runtime/TaskBucketEntryPoint_Ops.res +31 -37
  117. package/src/adapter/Runtime/TaskRuntime_Builder_PerBucket.res +6 -7
  118. package/src/adapter/ScheduledPublisher/ScheduledPublisher_CloudWatchEvents.res +4 -6
  119. package/src/adapter/ScheduledPublisher/ScheduledPublisher_CloudWatchEvents_Runtime.res +2 -6
  120. package/src/adapter/StateTopic/StateTopic_AppSync.res +21 -16
  121. package/src/adapter/StateTopic/StateTopic_AppSync_Ops.res +36 -32
  122. package/src/adapter/Task/TaskBucket_S3.res +6 -1
  123. package/src/adapter/Upload/Upload_Claim_S3.res +17 -20
  124. package/src/adapter/Upload/Upload_Claim_S3_Ops.res +5 -2
  125. package/src/adapter/Upload/Upload_Presign_S3.res +51 -49
  126. package/src/capability/Capability_Geocoding_AwsLocation.res +4 -1
  127. package/src/capability/Capability_Messaging.res +4 -5
  128. package/src/capability/Capability_Messaging_Ses.res +3 -1
  129. package/src/capability/Capability_ObjectStore_S3.res +1 -7
  130. package/src/components/Api/AppSync_Adapter.res +80 -109
  131. package/src/components/Api/AppSync_Adapter.res.mjs +26 -60
  132. package/src/components/Api/AppSync_SdlDecorate.res +118 -31
  133. package/src/components/Api/AppSync_SdlDecorate.res.mjs +45 -0
  134. package/src/components/AutomationSlice_Builder.res +6 -4
  135. package/src/components/ExtensionPoint_Builder.res +9 -10
  136. package/src/components/InboundTranslationSlice_Builder.res +7 -8
  137. package/src/components/OutboundTranslationSlice_Builder.res +14 -8
  138. package/src/components/OutboundTranslationSlice_Builder.res.mjs +1 -1
  139. package/src/components/SideEffectHandler_Single.res +4 -4
  140. package/src/components/StateViewSlice_Builder.res +6 -4
  141. package/src/components/StateViewSlice_Builder_Stream.res +6 -4
  142. package/src/components/Task_Builder_PerBucket.res +10 -11
  143. package/src/plugin/cloner/ClonerRunner_Fargate.res +3 -10
  144. package/src/plugin/cloner/ClonerRunner_Fargate_Runtime.res +1 -4
  145. package/src/plugin/heartbeat/HeartbeatRunner_CloudWatchEvents.res +5 -5
  146. package/src/plugin/runtime/PluginExtensionPointRuntime_Builder.res +19 -21
  147. package/src/plugin/runtime/PluginRuntime_Builder.res +167 -188
  148. package/src/plugin/runtime/Util_PluginMessage_Runtime.res +6 -5
  149. package/src/plugin/stack/Plugin_Stack.res +104 -103
  150. package/src/util/Util_AlarmSpec.res +2 -1
  151. package/src/util/Util_AppSync.res +8 -2
  152. package/src/util/Util_AppSync_Caller.res +15 -14
  153. package/src/util/Util_Bundle.res +46 -42
  154. package/src/util/Util_CognitoGroupUser_Runtime.res +32 -34
  155. package/src/util/Util_Cognito_Runtime.res +11 -7
  156. package/src/util/Util_DeadLetterQueue.res +12 -2
  157. package/src/util/Util_DynamoDb.res +5 -3
  158. package/src/util/Util_DynamoDbStream.res +7 -3
  159. package/src/util/Util_DynamoDbStream_Runtime.res +1 -2
  160. package/src/util/Util_DynamoDb_Runtime.res +23 -41
  161. package/src/util/Util_HostUiDomain.res +6 -1
  162. package/src/util/Util_Kinesis_Runtime.res +10 -11
  163. package/src/util/Util_Lambda.res +12 -5
  164. package/src/util/Util_LambdaEnvBudget.res +5 -5
  165. package/src/util/Util_LambdaLogging.res +1 -2
  166. package/src/util/Util_LogGroup_Adopting.res +1 -4
  167. package/src/util/Util_ResourceNaming.res +1 -2
  168. package/src/util/Util_SNS.res +8 -3
  169. package/src/util/Util_SNS_FIFO.res +1 -1
  170. package/src/util/Util_SNS_Runtime.res +11 -10
  171. package/src/util/Util_SQS.res +4 -2
  172. package/src/util/Util_SQS_FIFO.res +5 -2
  173. package/src/util/Util_SQS_Runtime.res +37 -36
  174. package/src/util/Util_ShellConfig.res +3 -2
  175. package/src/util/Util_StaticBundle.res +1 -3
  176. package/src/util/Util_StoreLayout.res +4 -1
  177. package/tests/AWS_TagsTest.res +16 -5
  178. package/tests/AppSyncEventsSigner_OpsTest.res +11 -2
  179. package/tests/AppSync_AdapterTest.res +286 -224
  180. package/tests/AppSync_AdapterTest.res.mjs +69 -0
  181. package/tests/AppSync_Resolver_RetryingTest.res +64 -44
  182. package/tests/AppSync_RetirementNarrowingTest.res +59 -46
  183. package/tests/AppSync_SdlDecorateTest.res +22 -20
  184. package/tests/AppSync_SourceApiAssociation_RetryingTest.res +40 -22
  185. package/tests/Auth_ActiveRolePoolAttachmentTest.res +114 -107
  186. package/tests/Auth_ActiveRoleStoreTest.res +12 -9
  187. package/tests/Auth_ActiveRoleStore_SchemaTest.res +2 -1
  188. package/tests/Auth_ActiveRoleTrigger_OpsTest.res +20 -16
  189. package/tests/Auth_CognitoTest.res +31 -46
  190. package/tests/Auth_LoginIdentifierTest.res +51 -0
  191. package/tests/Auth_LoginIdentifierTest.res.mjs +51 -0
  192. package/tests/Auth_SignUpModeTest.res +56 -0
  193. package/tests/Auth_SignUpModeTest.res.mjs +53 -0
  194. package/tests/AutomationSliceEntryPoint_OpsTest.res +69 -67
  195. package/tests/BakeConvergenceTest.res +22 -25
  196. package/tests/Capability_MessagingTest.res +28 -12
  197. package/tests/CommandTopicQueuePolicyTest.res +1 -2
  198. package/tests/CounterEntryPoint_OpsTest.res +3 -7
  199. package/tests/DcbEventLogStorage_DynamoDb_RuntimeTest.res +306 -210
  200. package/tests/EventCollectorConnectLambdaPreviewTest.res +14 -7
  201. package/tests/EventCollectorEntryPoint_OpsTest.res +12 -4
  202. package/tests/EventCollectorPluginDefinitionAssetTest.res +18 -11
  203. package/tests/EventCollectorReadyGateTest.res +5 -4
  204. package/tests/EventLogStorage_DynamoDb_RuntimeTest.res +1 -2
  205. package/tests/EventMapperEntryPoint_OpsTest.res +1 -3
  206. package/tests/ExtensionPointEntryPoint_OpsTest.res +1 -4
  207. package/tests/HandlerFactoryDispatchTest.res +97 -70
  208. package/tests/MCP_LambdaTest.res +16 -27
  209. package/tests/PgChangeFeedRelay_IntegrationTest.res +123 -93
  210. package/tests/PgChangeFeedRelay_RuntimeTest.res +13 -10
  211. package/tests/PgMigrationEntryPoint_IntegrationTest.res +0 -1
  212. package/tests/PgPipeline_IntegrationTest.res +80 -66
  213. package/tests/PgQueryDbUnion_IntegrationTest.res +1 -2
  214. package/tests/PgQueryResolverEntryPoint_OpsTest.res +2 -6
  215. package/tests/PgQueryResolver_LambdaTest.res +41 -24
  216. package/tests/Platform_AdminScan_OpsTest.res +4 -1
  217. package/tests/Platform_ComponentDefinitions_Lambda_OpsTest.res +137 -129
  218. package/tests/ProjectionEntryPoint_DisplayNameTest.res +7 -3
  219. package/tests/ProjectionEntryPoint_UnionStampTest.res +3 -2
  220. package/tests/ProvisionAccountsTest.res +142 -0
  221. package/tests/ProvisionAccountsTest.res.mjs +153 -0
  222. package/tests/ProvisionAdminTest.res +55 -0
  223. package/tests/ProvisionAdminTest.res.mjs +66 -0
  224. package/tests/ProvisionIdentityTest.res +91 -9
  225. package/tests/ProvisionIdentityTest.res.mjs +66 -1
  226. package/tests/QueryDbOwnerIndexTableTest.res +6 -4
  227. package/tests/QueryDbResolvers_AppSyncTest.res +23 -16
  228. package/tests/QueryInterceptor_LambdaTest.res +11 -13
  229. package/tests/ReadModelEntryPoint_OpsTest.res +43 -41
  230. package/tests/RuntimeExtensionEntryPoint_OpsTest.res +4 -3
  231. package/tests/SideEffectEntryPoint_OpsTest.res +23 -18
  232. package/tests/StateChangeDescriptorParityTest.res +28 -26
  233. package/tests/StateTopicKeySchemaTest.res +18 -15
  234. package/tests/StateViewSliceEntryPoint_OpsTest.res +55 -44
  235. package/tests/TaskBucketEntryPoint_OpsTest.res +13 -13
  236. package/tests/Upload_ClaimTest.res +3 -3
  237. package/tests/Util_AwsErrorTest.res +4 -7
  238. package/tests/Util_BundleDependencyClosureTest.res +15 -3
  239. package/tests/Util_BundleEsmLoaderTest.res +3 -3
  240. package/tests/Util_BundleResolveTest.res +4 -6
  241. package/tests/Util_BundleRuntimeExtensionTest.res +17 -18
  242. package/tests/Util_DcbMetricsTest.res +6 -6
  243. package/tests/Util_DynamoDbStreamTest.res +4 -5
  244. package/tests/Util_DynamoDbTest.res +3 -2
  245. package/tests/Util_LambdaEnvBudgetTest.res +22 -18
  246. package/tests/Util_LambdaLoggingTest.res +9 -8
  247. package/tests/Util_LogGroup_AdoptingTest.res +10 -5
  248. package/tests/Util_LogRetentionTest.res +6 -2
  249. package/tests/Util_OwnerScopeEnvTest.res +35 -8
  250. package/tests/Util_OwnerScopeEnvTest.res.mjs +13 -0
  251. package/tests/Util_SQS_RuntimeTest.res +5 -7
  252. package/tests/Util_ShellConfigTest.res +7 -17
  253. package/tests/Util_StaticBundleTest.res +3 -8
  254. package/tests/Util_StoreLayoutTest.res +17 -12
  255. package/tests/integration/AggregateEntryPoint_IntegrationTest.res +1 -4
  256. package/tests/integration/DcbCommandTopicEntryPoint_IntegrationTest.res +15 -15
  257. package/tests/integration/DcbEventLogStorage_DynamoDb_IntegrationTest.res +296 -232
  258. package/tests/integration/DcbIntegrationHarness.res +8 -8
  259. package/tests/integration/EpCompositeSlice.res +16 -8
  260. package/tests/integration/QueryDbUnion_DynamoDb_IntegrationTest.res +8 -10
  261. package/tests/integration/StateViewSliceEntryPoint_IntegrationTest.res +10 -10
  262. package/tests/integration/SvsTestSlice_Projection.res +1 -3
  263. package/tests/util/Util_AlarmSpecTest.res +62 -49
package/src/Platform.res CHANGED
@@ -121,6 +121,31 @@ module MakeWithConfig = (
121
121
  ): (
122
122
  ReventlessInfra.Platform.T with type api = Types.AppSync.api and type role = Types.AppSync.role
123
123
  ) => {
124
+ // The administrator group reads across every owner, unless the deployment says
125
+ // otherwise.
126
+ //
127
+ // Authorization and owner scoping decide admin-ness independently and nothing
128
+ // checks that they agree: without this, a deployment gets an administrator who
129
+ // passes every authorization check and then reads *empty* owner-scoped views.
130
+ // `Util_OwnerScopeEnv.applyElevatedGroupsDefault` already carries whatever
131
+ // `OwnerScope.elevatedGroups()` holds into every Lambda's environment — the pipe
132
+ // was laid and nothing ever put a value in.
133
+ //
134
+ // 🚨 **Here, at the entry point, rather than beside the pool that declares the
135
+ // group.** The value has to be set before the runtime builder composes any
136
+ // Lambda's environment, and this functor's body runs when the functor is
137
+ // applied — necessarily before any plugin is built through it. Written into
138
+ // `Platform_Stack.resolveCognitoUserPool` it would instead depend on *which
139
+ // caller resolved the pool first*, which is precisely how a feature once
140
+ // vanished from a deploy with no error and no resource; see the note on
141
+ // `_resolveUncached`. Ordering that is structural beats ordering that happens
142
+ // to hold.
143
+ //
144
+ // A default, never a forcing: an explicit `setElevatedGroups` in a platform
145
+ // root — including one naming nobody — means what it says and wins. See
146
+ // [Reventless.OwnerScope.defaultElevatedGroups].
147
+ Reventless.OwnerScope.defaultElevatedGroups([Reventless.AdminGroup.name])
148
+
124
149
  // Dispatch per-flavor commandHandlerConfig records to the four runtime
125
150
  // builders. Every sub-record is optional; only branches the caller actually
126
151
  // supplied propagate, and within each sub-record every field is optional too
@@ -201,8 +226,10 @@ module MakeWithConfig = (
201
226
  // A platform that provisions none omits the export entirely, which reads
202
227
  // as "no store to claim in" — the claimer then builds nothing, rather
203
228
  // than building something that can reach nothing.
204
- (stackRef->Pulumi.StackReference.getOutput("objectStores"): Pulumi.Output.t<option<JSON.t>>)
205
- ->Pulumi.Output.apply(objectStores =>
229
+
230
+ (
231
+ stackRef->Pulumi.StackReference.getOutput("objectStores"): Pulumi.Output.t<option<JSON.t>>
232
+ )->Pulumi.Output.apply(objectStores =>
206
233
  objectStores
207
234
  ->Option.flatMap(JSON.Decode.object)
208
235
  ->Option.getOr(Dict.make())
@@ -210,15 +237,16 @@ module MakeWithConfig = (
210
237
  ->Array.filterMap(((qualified, v)) =>
211
238
  v
212
239
  ->JSON.Decode.object
213
- ->Option.flatMap(o =>
214
- switch (
215
- o->Dict.get("bucketName")->Option.flatMap(JSON.Decode.string),
216
- o->Dict.get("keyPrefix")->Option.flatMap(JSON.Decode.string),
217
- ) {
218
- | (Some(bucketName), Some(servedPrefix)) =>
219
- Some({Upload_Claim_S3.qualified: qualified, bucketName, servedPrefix})
220
- | _ => None
221
- }
240
+ ->Option.flatMap(
241
+ o =>
242
+ switch (
243
+ o->Dict.get("bucketName")->Option.flatMap(JSON.Decode.string),
244
+ o->Dict.get("keyPrefix")->Option.flatMap(JSON.Decode.string),
245
+ ) {
246
+ | (Some(bucketName), Some(servedPrefix)) =>
247
+ Some({Upload_Claim_S3.qualified, bucketName, servedPrefix})
248
+ | _ => None
249
+ },
222
250
  )
223
251
  )
224
252
  )
@@ -273,8 +301,9 @@ module MakeWithConfig = (
273
301
  // shared types win over every plugin source's standalone copy (divergence
274
302
  // is shadowed, not MERGE_FAILED — Phase-0 finding 1).
275
303
  let assembleCanonicalSourceSdl = (~baseFragment): string =>
276
- AppSync_Adapter.stitchStandaloneWithAwsDirectives(~fragment=baseFragment)
277
- ->AppSync_SdlDecorate.stampCanonicalTypes
304
+ AppSync_Adapter.stitchStandaloneWithAwsDirectives(
305
+ ~fragment=baseFragment,
306
+ )->AppSync_SdlDecorate.stampCanonicalTypes
278
307
 
279
308
  // Admin base as a source-API document — auth-decorated (all fields Admin,
280
309
  // Cognito-only; the deploy-time SigV4 system-caller fields died with the
@@ -283,13 +312,14 @@ module MakeWithConfig = (
283
312
  assembleCanonicalSourceSdl(
284
313
  ~baseFragment=AppSync_Adapter.injectAwsAuthAll(
285
314
  ReventlessCore.Platform_AdminApi.baseFragment(~cloner=Config.cloner),
286
- ~group="Admin",
315
+ ~group=Reventless.AdminGroup.name,
287
316
  ),
288
317
  )
289
318
 
290
319
  // Split-mode Domain source document: relay base types + Platform_ping — the
291
320
  // Domain merged API's canonical owner (plugin fields come from plugin sources).
292
- let domainBaseSourceSdl = (): string => assembleCanonicalSourceSdl(~baseFragment=domainBaseFragment)
321
+ let domainBaseSourceSdl = (): string =>
322
+ assembleCanonicalSourceSdl(~baseFragment=domainBaseFragment)
293
323
 
294
324
  let (domainApi, domainApiRole, platformApi, platformApiRole) = switch platformStackRef {
295
325
  | None =>
@@ -337,37 +367,40 @@ module MakeWithConfig = (
337
367
  defaultOutput,
338
368
  )
339
369
  ->Pulumi.Output.all3
340
- ->Pulumi.Output.apply(
341
- (((directProviderId, directProviderRegion), (directPoolId, directRegion), default)) => {
342
- let getFromDefault = key =>
343
- default
344
- ->Option.flatMap(d => d->JSON.Decode.object)
345
- ->Option.flatMap(d => d->Dict.get(key))
346
- ->Option.flatMap(v => v->JSON.Decode.string)
347
- // New spelling first at both levels, then the deprecated one. In ESM
348
- // mode exports arrive nested under "default", hence each pair.
349
- let userPoolId =
350
- directProviderId
351
- ->Option.orElse(getFromDefault("identityProviderId"))
352
- ->Option.orElse(directPoolId)
353
- ->Option.orElse(getFromDefault("cognitoUserPoolId"))
354
- ->Option.getOrThrow(
355
- ~message="Platform stack exports neither 'identityProviderId' nor 'cognitoUserPoolId' — redeploy the platform stack first",
356
- )
357
- let awsRegion =
358
- directProviderRegion
359
- ->Option.orElse(getFromDefault("identityProviderRegion"))
360
- ->Option.orElse(directRegion)
361
- ->Option.orElse(getFromDefault("cognitoRegion"))
362
- (
363
- {
364
- userPoolId,
365
- ?awsRegion,
366
- defaultAction: PulumiAws.AppSync.GraphQLApi.ALLOW,
367
- }: PulumiAws.AppSync.GraphQLApi.userPoolConfig
370
+ ->Pulumi.Output.apply(((
371
+ (directProviderId, directProviderRegion),
372
+ (directPoolId, directRegion),
373
+ default,
374
+ )) => {
375
+ let getFromDefault = key =>
376
+ default
377
+ ->Option.flatMap(d => d->JSON.Decode.object)
378
+ ->Option.flatMap(d => d->Dict.get(key))
379
+ ->Option.flatMap(v => v->JSON.Decode.string)
380
+ // New spelling first at both levels, then the deprecated one. In ESM
381
+ // mode exports arrive nested under "default", hence each pair.
382
+ let userPoolId =
383
+ directProviderId
384
+ ->Option.orElse(getFromDefault("identityProviderId"))
385
+ ->Option.orElse(directPoolId)
386
+ ->Option.orElse(getFromDefault("cognitoUserPoolId"))
387
+ ->Option.getOrThrow(
388
+ ~message="Platform stack exports neither 'identityProviderId' nor 'cognitoUserPoolId' — redeploy the platform stack first",
368
389
  )
369
- },
370
- )
390
+ let awsRegion =
391
+ directProviderRegion
392
+ ->Option.orElse(getFromDefault("identityProviderRegion"))
393
+ ->Option.orElse(directRegion)
394
+ ->Option.orElse(getFromDefault("cognitoRegion"))
395
+
396
+ (
397
+ {
398
+ userPoolId,
399
+ ?awsRegion,
400
+ defaultAction: PulumiAws.AppSync.GraphQLApi.ALLOW,
401
+ }: PulumiAws.AppSync.GraphQLApi.userPoolConfig
402
+ )
403
+ })
371
404
  let (api, role) = AppSync_Adapter.makePluginSourceApiResource(
372
405
  ~name="PluginSourceApi",
373
406
  ~userPoolConfig,
@@ -384,72 +417,73 @@ module MakeWithConfig = (
384
417
  // Populate apiConfig with both domain and platform API references.
385
418
  // In platform/monolithic mode, platformApi starts as domainApi and is updated
386
419
  // by deployPlatform/makePlatform once the platform API resource is created (split mode).
387
- let () = apiConfigRef := Some({
388
- domainApi: domainApi,
389
- domainApiRole: domainApiRole,
390
- platformApi: platformApi,
391
- platformApiRole: platformApiRole,
392
- })
420
+ let () =
421
+ apiConfigRef :=
422
+ Some({
423
+ domainApi,
424
+ domainApiRole,
425
+ platformApi,
426
+ platformApiRole,
427
+ })
393
428
 
394
429
  // AppSync Events API — companion to the GraphQL API for Sources A and B subscriptions.
395
430
  // In platform/monolithic mode: created here as a real resource.
396
431
  // In plugin mode: reconstructed as a phantom from platform stack exports (eventsApiArn, eventsApiDns).
397
- let domainEventsApiOpt: option<AppSync_EventsApi.t> =
398
- switch platformStackRef {
399
- | None =>
400
- // Browser host-shell subscribes over WebSocket with a Cognito IdToken,
401
- // so the Events API must carry a Cognito auth provider. The pool is the
402
- // same process-cached one the GraphQL API + config.json use.
403
- let cognitoPool = Platform_Stack.resolveCognitoUserPool()
404
- let awsRegion =
405
- Pulumi.Config.make(Some("aws"))->Pulumi.Config.get("region")->Option.getOr("unknown")
406
- Some(
407
- AppSync_EventsApi.make(
408
- ~name="DomainEventsApi",
409
- ~cognitoUserPoolId=cognitoPool.poolId->Pulumi.Output.asInput,
410
- ~awsRegion,
411
- ~opts={},
412
- ),
432
+ let domainEventsApiOpt: option<AppSync_EventsApi.t> = switch platformStackRef {
433
+ | None =>
434
+ // Browser host-shell subscribes over WebSocket with a Cognito IdToken,
435
+ // so the Events API must carry a Cognito auth provider. The pool is the
436
+ // same process-cached one the GraphQL API + config.json use.
437
+ let cognitoPool = Platform_Stack.resolveCognitoUserPool()
438
+ let awsRegion =
439
+ Pulumi.Config.make(Some("aws"))->Pulumi.Config.get("region")->Option.getOr("unknown")
440
+ Some(
441
+ AppSync_EventsApi.make(
442
+ ~name="DomainEventsApi",
443
+ ~cognitoUserPoolId=cognitoPool.poolId->Pulumi.Output.asInput,
444
+ ~awsRegion,
445
+ ~opts={},
446
+ ),
447
+ )
448
+ | Some(stackRef) =>
449
+ let eventsApiArnOutput: Pulumi.Output.t<option<string>> =
450
+ stackRef->Pulumi.StackReference.getOutput("eventsApiArn")
451
+ let eventsApiDnsOutput: Pulumi.Output.t<option<string>> =
452
+ stackRef->Pulumi.StackReference.getOutput("eventsApiDns")
453
+ let defaultEventsOutput: Pulumi.Output.t<option<JSON.t>> =
454
+ stackRef->Pulumi.StackReference.getOutput("default")
455
+ let getFromDefault = (default, key) =>
456
+ default
457
+ ->Option.flatMap(d => d->JSON.Decode.object)
458
+ ->Option.flatMap(d => d->Dict.get(key))
459
+ ->Option.flatMap(v => v->JSON.Decode.string)
460
+ let apiArn =
461
+ (eventsApiArnOutput, defaultEventsOutput)
462
+ ->Pulumi.Output.all2
463
+ ->Pulumi.Output.apply(((direct, default)) =>
464
+ direct->Option.orElse(getFromDefault(default, "eventsApiArn"))
413
465
  )
414
- | Some(stackRef) =>
415
- let eventsApiArnOutput: Pulumi.Output.t<option<string>> =
416
- stackRef->Pulumi.StackReference.getOutput("eventsApiArn")
417
- let eventsApiDnsOutput: Pulumi.Output.t<option<string>> =
418
- stackRef->Pulumi.StackReference.getOutput("eventsApiDns")
419
- let defaultEventsOutput: Pulumi.Output.t<option<JSON.t>> =
420
- stackRef->Pulumi.StackReference.getOutput("default")
421
- let getFromDefault = (default, key) =>
422
- default
423
- ->Option.flatMap(d => d->JSON.Decode.object)
424
- ->Option.flatMap(d => d->Dict.get(key))
425
- ->Option.flatMap(v => v->JSON.Decode.string)
426
- let apiArn =
427
- (eventsApiArnOutput, defaultEventsOutput)
428
- ->Pulumi.Output.all2
429
- ->Pulumi.Output.apply(((direct, default)) =>
430
- direct->Option.orElse(getFromDefault(default, "eventsApiArn"))
431
- )
432
- let dns =
433
- (eventsApiDnsOutput, defaultEventsOutput)
434
- ->Pulumi.Output.all2
435
- ->Pulumi.Output.apply(((direct, default)) =>
436
- direct->Option.orElse(getFromDefault(default, "eventsApiDns"))
437
- )
438
- let api: PulumiAws.AwsNative.AppSync.Api.t = {
439
- apiId: Pulumi.Output.make(""),
440
- apiArn: apiArn->Pulumi.Output.apply(v => v->Option.getOr("")),
441
- dns: dns->Pulumi.Output.apply(dnsHttp => ({
442
- PulumiAws.AwsNative.AppSync.Api.http: ?dnsHttp,
443
- }: PulumiAws.AwsNative.AppSync.Api.dns)),
444
- name: Pulumi.Output.make(""),
445
- }
446
- let eventsApi: AppSync_EventsApi.t = {
447
- name: "DomainEventsApi",
448
- api,
449
- defaultNamespace: None,
450
- }
451
- Some(eventsApi)
466
+ let dns =
467
+ (eventsApiDnsOutput, defaultEventsOutput)
468
+ ->Pulumi.Output.all2
469
+ ->Pulumi.Output.apply(((direct, default)) =>
470
+ direct->Option.orElse(getFromDefault(default, "eventsApiDns"))
471
+ )
472
+ let api: PulumiAws.AwsNative.AppSync.Api.t = {
473
+ apiId: Pulumi.Output.make(""),
474
+ apiArn: apiArn->Pulumi.Output.apply(v => v->Option.getOr("")),
475
+ dns: dns->Pulumi.Output.apply((dnsHttp): PulumiAws.AwsNative.AppSync.Api.dns => {
476
+ PulumiAws.AwsNative.AppSync.Api.http: ?dnsHttp,
477
+ }),
478
+ name: Pulumi.Output.make(""),
479
+ }
480
+ let eventsApi: AppSync_EventsApi.t = {
481
+ name: "DomainEventsApi",
482
+ api,
483
+ defaultNamespace: None,
452
484
  }
485
+ Some(eventsApi)
486
+ }
453
487
 
454
488
  // B3.3: publish the events-API endpoint + ARN to the projection-Lambda runtime
455
489
  // builders, so a subscription-enabled Postgres read model / view slice gets the
@@ -509,8 +543,7 @@ module MakeWithConfig = (
509
543
  EventMappings: ReventlessInfra.EventMapper.Mappings with module Target := Spec,
510
544
  ): (
511
545
  ReventlessInfra.Aggregate.T with type api = Types.AppSync.api
512
- ) =>
513
- Aggregate_Builder_Single.Make(Spec, Behavior, EventMappings)
546
+ ) => Aggregate_Builder_Single.Make(Spec, Behavior, EventMappings)
514
547
  /** Async variant — uses FIFO SQS channel, commands return `CommandPending`. */
515
548
  module MakeAsync = (
516
549
  Spec: Reventless.Aggregate.Spec,
@@ -518,8 +551,7 @@ module MakeWithConfig = (
518
551
  EventMappings: ReventlessInfra.EventMapper.Mappings with module Target := Spec,
519
552
  ): (
520
553
  ReventlessInfra.Aggregate.T with type api = Types.AppSync.api
521
- ) =>
522
- Aggregate_Builder_Single_Async.Make(Spec, Behavior, EventMappings)
554
+ ) => Aggregate_Builder_Single_Async.Make(Spec, Behavior, EventMappings)
523
555
  }
524
556
 
525
557
  module ReadModel = {
@@ -531,8 +563,7 @@ module MakeWithConfig = (
531
563
  with module Spec = Spec
532
564
  and type api = Types.AppSync.api
533
565
  and type role = Types.AppSync.role
534
- ) =>
535
- ReadModel_Builder_Single.Make(Spec, Mappings)
566
+ ) => ReadModel_Builder_Single.Make(Spec, Mappings)
536
567
  }
537
568
 
538
569
  /** Stream-enabled read model — DynamoDB Stream + StateTopic Lambda for
@@ -546,8 +577,7 @@ module MakeWithConfig = (
546
577
  with module Spec = Spec
547
578
  and type api = Types.AppSync.api
548
579
  and type role = Types.AppSync.role
549
- ) =>
550
- ReadModel_Builder_Single_Stream.Make(Spec, Mappings)
580
+ ) => ReadModel_Builder_Single_Stream.Make(Spec, Mappings)
551
581
  }
552
582
 
553
583
  module ExtensionPoint = {
@@ -584,8 +614,7 @@ module MakeWithConfig = (
584
614
  module Mappings: ReventlessInfra.ExtensionPoint.Mappings with module Spec := Spec = {
585
615
  module type Mapping = ReventlessInfra.ExtensionPointMapping.T
586
616
  with module ExtensionPoint := Spec
587
- let name =
588
- Mapping1.Delegate.name ++ "+" ++ Mapping2.Delegate.name
617
+ let name = Mapping1.Delegate.name ++ "+" ++ Mapping2.Delegate.name
589
618
  // First mapping's URL — matches the user-extension merge convention.
590
619
  // See Make's moduleUrl comment for the %raw rationale.
591
620
  let moduleUrl = Mapping1.moduleUrl
@@ -610,11 +639,7 @@ module MakeWithConfig = (
610
639
  module type Mapping = ReventlessInfra.ExtensionPointMapping.T
611
640
  with module ExtensionPoint := Spec
612
641
  let name =
613
- Mapping1.Delegate.name ++
614
- "+" ++
615
- Mapping2.Delegate.name ++
616
- "+" ++
617
- Mapping3.Delegate.name
642
+ Mapping1.Delegate.name ++ "+" ++ Mapping2.Delegate.name ++ "+" ++ Mapping3.Delegate.name
618
643
  // First mapping's URL — matches the user-extension merge convention.
619
644
  // See Make's moduleUrl comment for the %raw rationale.
620
645
  let moduleUrl = Mapping1.moduleUrl
@@ -636,14 +661,12 @@ module MakeWithConfig = (
636
661
  ): ReventlessInfra.Extension.Blueprint => {
637
662
  module Spec = Mapping.ExtensionPoint
638
663
  module CompiledMapping = ReventlessInfra.ExtensionMapping.Make(Mapping)
639
- module type Mapping = ReventlessInfra.ExtensionMapping.T
640
- with module ExtensionPoint := Spec
664
+ module type Mapping = ReventlessInfra.ExtensionMapping.T with module ExtensionPoint := Spec
641
665
  let name = Mapping.Delegate.name
642
666
  let moduleUrl = Mapping.moduleUrl
643
667
  let delegateModuleUrl = Mapping.delegateModuleUrl
644
668
  let mappings: array<module(Mapping)> = [module(CompiledMapping)]
645
669
  }
646
-
647
670
  }
648
671
 
649
672
  module Task = {
@@ -658,8 +681,9 @@ module MakeWithConfig = (
658
681
  module Make = (
659
682
  Spec: Reventless.StateChangeSlice.Spec,
660
683
  Behavior: Reventless.StateChangeSlice.Behavior with module Spec := Spec,
661
- ): (ReventlessInfra.StateChangeSlice.T with module Spec = Spec) =>
662
- StateChangeSlice_Builder.Make(Spec, Behavior)
684
+ ): (
685
+ ReventlessInfra.StateChangeSlice.T with module Spec = Spec
686
+ ) => StateChangeSlice_Builder.Make(Spec, Behavior)
663
687
  /** Async variant — uses FIFO SQS channel, commands return `CommandPending`. */
664
688
  module MakeAsync = (
665
689
  Spec: Reventless.StateChangeSlice.Spec,
@@ -742,7 +766,6 @@ module MakeWithConfig = (
742
766
  | None => resolveHookedApi()
743
767
  }
744
768
 
745
-
746
769
  let hooks: ReventlessCore.Plugin_Helpers.platformHooks = {
747
770
  // AWS uses Interstack for admin extension points — leave ref at empty dict.
748
771
  adminExtensionPoints: ref(Pulumi.Output.make(Dict.make())),
@@ -776,7 +799,6 @@ module MakeWithConfig = (
776
799
  ~opts,
777
800
  )
778
801
  },
779
-
780
802
  // Admin-side schema push, gated on admin DataSources via adminBarrier.
781
803
  // Platform_Admin.construct invokes this and chains createResolvers behind
782
804
  // the returned Output — so admin CreateResolver calls fire only after
@@ -793,7 +815,6 @@ module MakeWithConfig = (
793
815
  // before the resource resolves, so admin resolvers chained on the API are
794
816
  // already ordered after the schema is ACTIVE. No push.
795
817
  preAdminResolversSchemaHook: (~adminBarrier) => adminBarrier,
796
-
797
818
  // Push the plugin's standalone subgraph document to the plugin's OWN
798
819
  // source API — a single writer by construction. The returned Output gates
799
820
  // resolver creation and deployPlugin additionally sequences the
@@ -802,23 +823,24 @@ module MakeWithConfig = (
802
823
  // re-merges automatically.
803
824
  preResolversSchemaHook: (~name, ~version, pluginFragment) => {
804
825
  let sdl = AppSync_Adapter.stitchStandaloneWithAwsDirectives(~fragment=pluginFragment)
805
- let pushed =
806
- domainApi->Pulumi.Output.flatMap(api =>
807
- api.id->Pulumi.Output.flatMap(apiId => {
808
- log.info(
809
- ~comp="preResolversSchemaHook",
810
- `Pushing subgraph schema for ${name}@${version} to source API ${apiId}`,
811
- )
812
- let client = AppSync_Adapter.getClient()
813
- client
814
- ->AppSync_Adapter.startSchemaCreationRetrying({apiId, definition: sdl})
815
- ->Promise.then(async _ => {
826
+ let pushed = domainApi->Pulumi.Output.flatMap(api =>
827
+ api.id->Pulumi.Output.flatMap(apiId => {
828
+ log.info(
829
+ ~comp="preResolversSchemaHook",
830
+ `Pushing subgraph schema for ${name}@${version} to source API ${apiId}`,
831
+ )
832
+ let client = AppSync_Adapter.getClient()
833
+ client
834
+ ->AppSync_Adapter.startSchemaCreationRetrying({apiId, definition: sdl})
835
+ ->Promise.then(
836
+ async _ => {
816
837
  await AppSync_Adapter.waitForSchemaActive(client, apiId)
817
838
  log.info(~comp="preResolversSchemaHook", "subgraph schema is ACTIVE")
818
- })
819
- ->Pulumi.Output.fromPromise
820
- })
821
- )
839
+ },
840
+ )
841
+ ->Pulumi.Output.fromPromise
842
+ })
843
+ )
822
844
  mergedSchemaPushedRef := Some(pushed)
823
845
  pushed
824
846
  },
@@ -864,98 +886,102 @@ module MakeWithConfig = (
864
886
  ~epQueueUrl=resource.id->Pulumi.Output.make,
865
887
  (),
866
888
  )
867
- | None =>
868
- log.warn(~comp="Platform", "heartbeat EP channel has no resources")
889
+ | None => log.warn(~comp="Platform", "heartbeat EP channel has no resources")
869
890
  }
870
891
  },
871
-
872
892
  // Phase 4 + 5: wire StateTopic and EventLogSubscription Lambdas per plugin.
873
893
  // Only active when the Events API resource exists (platform/monolithic mode).
874
- subscriptionInfraHook: ?domainEventsApiOpt->Option.map(eventsApi =>
875
- (params: ReventlessCore.Plugin_Helpers.subscriptionInfraParams) => {
876
- let {pluginName, allQueryDbs, allEventTopics, eventLogEntries, opts} = params
877
- let customOpts =
878
- opts->ReventlessCore.Util.Pulumi.ComponentResourceOptions.toCustomResourceOptions
879
- // StateTopic Lambda per stream-enabled QueryDb — publishes row changes
880
- // to AppSync Events channels. No GraphQL Subscription resolver is
881
- // wired: Source B uses the Events WebSocket directly, not @aws_subscribe.
882
- allQueryDbs->Dict.forEachWithKey((_queryDbOutputs, readModelName) => {
883
- if QueryDbStorage_DynamoDbStream.streamRegistry->Set.has(readModelName) {
884
- // Channel root MUST equal what the host-shell subscribes to. The
885
- // AutoUI manifest sets queryableDef.queryField = listFieldName
886
- // (plural, e.g. "Catalog_Products") and AutoLive subscribes on that.
887
- // Publishing on the singular returnTypeName ("Catalog_Product")
888
- // would land descriptors on a channel no client listens to.
889
- let topicName =
890
- ReventlessCore.Plugin_Helpers.queryFieldNamesRegistry
891
- ->Dict.get(readModelName)
892
- ->Option.map(qn => qn.listFieldName)
893
- ->Option.getOr(readModelName)
894
- StateTopic_AppSync.make(
895
- ~readModelName,
896
- ~topicName,
897
- ~allQueryDbs,
898
- ~eventsApi,
899
- ~opts=customOpts,
900
- )
901
- }
902
- })
894
+ subscriptionInfraHook: ?(
895
+ domainEventsApiOpt->Option.map(eventsApi =>
896
+ (params: ReventlessCore.Plugin_Helpers.subscriptionInfraParams) => {
897
+ let {pluginName, allQueryDbs, allEventTopics, eventLogEntries, opts} = params
898
+ let customOpts =
899
+ opts->ReventlessCore.Util.Pulumi.ComponentResourceOptions.toCustomResourceOptions
900
+ // StateTopic Lambda per stream-enabled QueryDb — publishes row changes
901
+ // to AppSync Events channels. No GraphQL Subscription resolver is
902
+ // wired: Source B uses the Events WebSocket directly, not @aws_subscribe.
903
+ allQueryDbs->Dict.forEachWithKey((_queryDbOutputs, readModelName) => {
904
+ if QueryDbStorage_DynamoDbStream.streamRegistry->Set.has(readModelName) {
905
+ // Channel root MUST equal what the host-shell subscribes to. The
906
+ // AutoUI manifest sets queryableDef.queryField = listFieldName
907
+ // (plural, e.g. "Catalog_Products") and AutoLive subscribes on that.
908
+ // Publishing on the singular returnTypeName ("Catalog_Product")
909
+ // would land descriptors on a channel no client listens to.
910
+ let topicName =
911
+ ReventlessCore.Plugin_Helpers.queryFieldNamesRegistry
912
+ ->Dict.get(readModelName)
913
+ ->Option.map(qn => qn.listFieldName)
914
+ ->Option.getOr(readModelName)
915
+ StateTopic_AppSync.make(
916
+ ~readModelName,
917
+ ~topicName,
918
+ ~allQueryDbs,
919
+ ~eventsApi,
920
+ ~opts=customOpts,
921
+ )
922
+ }
923
+ })
903
924
 
904
- // Phase 5: EventLogSubscription per SNS-backed event log entry.
905
- // DynamoDB stream event topics (Category, DCB) are skipped — no SNS subscription needed.
906
- eventLogEntries->Array.forEach(entry => {
907
- // Aggregate EventTopics are keyed by Spec.name (= displayName).
908
- // DCB EventTopic is keyed by busKey (= pluginName ++ "DcbEventLog").
909
- let isSns =
910
- EventTopicPublisher_SNS.snsRegistry->Set.has(entry.displayName) ||
911
- EventTopicPublisher_SNS.snsRegistry->Set.has(entry.busKey)
912
- let topicOutputs =
913
- allEventTopics
914
- ->Dict.get(entry.displayName)
915
- ->Option.orElse(allEventTopics->Dict.get(entry.busKey))
916
- isSns
917
- ? topicOutputs->Option.forEach(outputs =>
918
- EventLogSubscription_AppSync.make(
919
- ~name=entry.displayName,
920
- ~topicName=entry.displayName,
921
- ~eventTopicOutputs=outputs,
922
- ~eventsApi,
923
- ~opts=customOpts,
925
+ // Phase 5: EventLogSubscription per SNS-backed event log entry.
926
+ // DynamoDB stream event topics (Category, DCB) are skipped — no SNS subscription needed.
927
+ eventLogEntries->Array.forEach(entry => {
928
+ // Aggregate EventTopics are keyed by Spec.name (= displayName).
929
+ // DCB EventTopic is keyed by busKey (= pluginName ++ "DcbEventLog").
930
+ let isSns =
931
+ EventTopicPublisher_SNS.snsRegistry->Set.has(entry.displayName) ||
932
+ EventTopicPublisher_SNS.snsRegistry->Set.has(entry.busKey)
933
+ let topicOutputs =
934
+ allEventTopics
935
+ ->Dict.get(entry.displayName)
936
+ ->Option.orElse(allEventTopics->Dict.get(entry.busKey))
937
+ isSns
938
+ ? topicOutputs->Option.forEach(
939
+ outputs =>
940
+ EventLogSubscription_AppSync.make(
941
+ ~name=entry.displayName,
942
+ ~topicName=entry.displayName,
943
+ ~eventTopicOutputs=outputs,
944
+ ~eventsApi,
945
+ ~opts=customOpts,
946
+ ),
924
947
  )
925
- )
926
- : ()
927
- // Register this event log with the upload claimer if — and only if —
928
- // its events declare a `@storageRef` field. An event log that declares
929
- // none is not registered, so a platform whose plugins declare no store
930
- // provisions no claimer at all. `isSns` is the same synchronous answer
931
- // the subscription above turns on, so the two cannot disagree about
932
- // which channel an event log publishes through.
933
- topicOutputs->Option.forEach(outputs =>
934
- Upload_Claim_S3.make(
935
- ~plugin=pluginName,
936
- ~eventLogName=entry.displayName,
937
- ~eventSchema=entry.eventSchema,
938
- ~eventTopicOutputs=outputs,
939
- ~isStreamBacked=!isSns,
948
+ : ()
949
+ // Register this event log with the upload claimer if — and only if —
950
+ // its events declare a `@storageRef` field. An event log that declares
951
+ // none is not registered, so a platform whose plugins declare no store
952
+ // provisions no claimer at all. `isSns` is the same synchronous answer
953
+ // the subscription above turns on, so the two cannot disagree about
954
+ // which channel an event log publishes through.
955
+ topicOutputs->Option.forEach(
956
+ outputs =>
957
+ Upload_Claim_S3.make(
958
+ ~plugin=pluginName,
959
+ ~eventLogName=entry.displayName,
960
+ ~eventSchema=entry.eventSchema,
961
+ ~eventTopicOutputs=outputs,
962
+ ~isStreamBacked=!isSns,
963
+ ),
940
964
  )
941
- )
942
- })
943
- // Finalize the shared StateTopic Lambda + IAM + per-stream ESMs from the
944
- // entries the loop above just registered. Must run INSIDE this hook because
945
- // Plugin_Builder fires it from inside a Pulumi.Output.apply chain — calling
946
- // `finish` from deployPlugin (after P.make returns) would see the registry
947
- // empty because the hook hasn't run yet. Admin's hook fires synchronously,
948
- // but the call site is unified here so admin and plugins share one path.
949
- StateTopic_AppSync.finish(~eventsApi, ~opts={})
950
- // Same timing constraint, same reason: the claimer's registry is filled
951
- // by the loop just above and drained here.
952
- Upload_Claim_S3.finish(~plugin=pluginName, ~stores=claimStores, ~opts=customOpts)
953
- }
965
+ })
966
+ // Finalize the shared StateTopic Lambda + IAM + per-stream ESMs from the
967
+ // entries the loop above just registered. Must run INSIDE this hook because
968
+ // Plugin_Builder fires it from inside a Pulumi.Output.apply chain — calling
969
+ // `finish` from deployPlugin (after P.make returns) would see the registry
970
+ // empty because the hook hasn't run yet. Admin's hook fires synchronously,
971
+ // but the call site is unified here so admin and plugins share one path.
972
+ StateTopic_AppSync.finish(~eventsApi, ~opts={})
973
+ // Same timing constraint, same reason: the claimer's registry is filled
974
+ // by the loop just above and drained here.
975
+ Upload_Claim_S3.finish(~plugin=pluginName, ~stores=claimStores, ~opts=customOpts)
976
+ }
977
+ )
954
978
  ),
955
979
  }
956
980
 
957
981
  // Apply Plugin functor with the platform hooks, then constrain the result to Plugin.T.
958
- module PluginBuilderImpl = Plugin.Make({let hooks = hooks})
982
+ module PluginBuilderImpl = Plugin.Make({
983
+ let hooks = hooks
984
+ })
959
985
  module Plugin: ReventlessInfra.Plugin.T
960
986
  with type api = Types.AppSync.api
961
987
  and type role = Types.AppSync.role
@@ -995,9 +1021,8 @@ module MakeWithConfig = (
995
1021
  // via the auto-resolver flow. Internal-protocol variants (Heartbeat, Connect,
996
1022
  // Disconnect, ReportIncompatibility) carry `@noApi` and are filtered out before
997
1023
  // SDL/resolver generation.
998
- module PluginAggregate: (
999
- ReventlessInfra.Aggregate.T with type api = Types.AppSync.api
1000
- ) = Aggregate_Builder_Single.Make(
1024
+ module PluginAggregate: ReventlessInfra.Aggregate.T
1025
+ with type api = Types.AppSync.api = Aggregate_Builder_Single.Make(
1001
1026
  ReventlessCore.PluginSpec,
1002
1027
  ReventlessCore.PluginBehavior,
1003
1028
  ReventlessInfra.NoEventMappings.Make(ReventlessCore.PluginSpec),
@@ -1061,8 +1086,9 @@ module MakeWithConfig = (
1061
1086
  // this is how RM/SVS projections are fed. One subscriber per (feed, log)
1062
1087
  // keeps checkpoints isolated.
1063
1088
  let feedTargets = (~connectionConfig, ~logName, ~feed, ~isClassic) =>
1064
- PgProjectionFeed.getFeedQueues()->Array.filterMap(fq =>
1065
- (isClassic ? fq.includeClassic : fq.includeDcb)
1089
+ PgProjectionFeed.getFeedQueues()->Array.filterMap(fq => {
1090
+ let included = isClassic ? fq.includeClassic : fq.includeDcb
1091
+ included
1066
1092
  ? Some({
1067
1093
  PgChangeFeedRelay_Builder.connectionConfig,
1068
1094
  logName,
@@ -1072,7 +1098,7 @@ module MakeWithConfig = (
1072
1098
  targetQueueArn: fq.arn,
1073
1099
  })
1074
1100
  : None
1075
- )
1101
+ })
1076
1102
  let dcbLogs = switch DcbBackend.get() {
1077
1103
  | Some({connectionConfig}) =>
1078
1104
  DcbBackend.getRelayLogs()->Array.flatMap(entry => {
@@ -1249,12 +1275,10 @@ module MakeWithConfig = (
1249
1275
  // deployPlatform (staged) and each plugin with deployPlugin — makePlatform
1250
1276
  // predates the merge path and never gained merged wiring.
1251
1277
  failwith(
1252
- "makePlatform is not supported on AWS — deploy the platform with deployPlatform and " ++
1253
- "each plugin with deployPlugin (merged-API composition).",
1278
+ "makePlatform is not supported on AWS — deploy the platform with deployPlatform and " ++ "each plugin with deployPlugin (merged-API composition).",
1254
1279
  )
1255
1280
  }
1256
1281
 
1257
-
1258
1282
  // Merged-mode outputs of deployPlatform — the merged API(s), plus the
1259
1283
  // deploy-time merge gates (Outputs that resolve on MERGE_SUCCESS and fail
1260
1284
  // the deploy on MERGE_FAILED; folded into the ARN exports so they are
@@ -1358,12 +1382,13 @@ module MakeWithConfig = (
1358
1382
  splitApiOutputsRef := Some({platformApi, platformApiRole})
1359
1383
  switch apiConfigRef.contents {
1360
1384
  | Some(c) =>
1361
- apiConfigRef := Some({
1362
- domainApi: c.domainApi,
1363
- domainApiRole: c.domainApiRole,
1364
- platformApi: platformApi,
1365
- platformApiRole: platformApiRole,
1366
- })
1385
+ apiConfigRef :=
1386
+ Some({
1387
+ domainApi: c.domainApi,
1388
+ domainApiRole: c.domainApiRole,
1389
+ platformApi,
1390
+ platformApiRole,
1391
+ })
1367
1392
  | None => ()
1368
1393
  }
1369
1394
  }
@@ -1434,21 +1459,18 @@ module MakeWithConfig = (
1434
1459
  // targets a non-existent ARN, breaking the
1435
1460
  // admin → plugin EventCollector → ConnectPlugin round-trip and leaving
1436
1461
  // the Plugin read model empty.
1437
- let pluginEpEventTopicArn =
1438
- admin.extensionPointsOutputs->Pulumi.Output.flatMap(eps =>
1439
- switch eps->Array.find(ep =>
1440
- ep.name == ReventlessInfra.PluginExtensionPointSpec.name
1441
- ) {
1442
- | Some(ep) =>
1443
- ep.eventTopic->Pulumi.Output.flatMap(et =>
1444
- switch et.resources->Array.get(0) {
1445
- | Some(r) => r.urn
1446
- | None => Pulumi.Output.make("NOT_AVAILABLE")
1447
- }
1448
- )
1449
- | None => Pulumi.Output.make("NOT_AVAILABLE")
1450
- }
1451
- )
1462
+ let pluginEpEventTopicArn = admin.extensionPointsOutputs->Pulumi.Output.flatMap(eps =>
1463
+ switch eps->Array.find(ep => ep.name == ReventlessInfra.PluginExtensionPointSpec.name) {
1464
+ | Some(ep) =>
1465
+ ep.eventTopic->Pulumi.Output.flatMap(et =>
1466
+ switch et.resources->Array.get(0) {
1467
+ | Some(r) => r.urn
1468
+ | None => Pulumi.Output.make("NOT_AVAILABLE")
1469
+ }
1470
+ )
1471
+ | None => Pulumi.Output.make("NOT_AVAILABLE")
1472
+ }
1473
+ )
1452
1474
 
1453
1475
  PluginRuntime_Builder.registerConfig(
1454
1476
  ~eventTopicArn=pluginEpEventTopicArn,
@@ -1555,7 +1577,10 @@ module MakeWithConfig = (
1555
1577
  api.uris->Pulumi.Output.apply(uris => uris.graphQL)
1556
1578
  ),
1557
1579
  )
1558
- Pulumi.Pulumi.export("platformApiRoleArn", platformApiRole->Pulumi.Output.flatMap(role => role.arn))
1580
+ Pulumi.Pulumi.export(
1581
+ "platformApiRoleArn",
1582
+ platformApiRole->Pulumi.Output.flatMap(role => role.arn),
1583
+ )
1559
1584
  } else {
1560
1585
  // Platform API exports (unified mode — same resource as Domain API).
1561
1586
  Pulumi.Pulumi.export("platformApiId", domainApi->Pulumi.Output.flatMap(api => api.id))
@@ -1565,16 +1590,17 @@ module MakeWithConfig = (
1565
1590
  api.uris->Pulumi.Output.apply(uris => uris.graphQL)
1566
1591
  ),
1567
1592
  )
1568
- Pulumi.Pulumi.export("platformApiRoleArn", domainApiRole->Pulumi.Output.flatMap(role => role.arn))
1593
+ Pulumi.Pulumi.export(
1594
+ "platformApiRoleArn",
1595
+ domainApiRole->Pulumi.Output.flatMap(role => role.arn),
1596
+ )
1569
1597
  }
1570
1598
 
1571
1599
  // Domain API exports.
1572
1600
  Pulumi.Pulumi.export("domainApiId", domainApi->Pulumi.Output.flatMap(api => api.id))
1573
1601
  Pulumi.Pulumi.export(
1574
1602
  "domainApiEndpoint",
1575
- domainApi->Pulumi.Output.flatMap(api =>
1576
- api.uris->Pulumi.Output.apply(uris => uris.graphQL)
1577
- ),
1603
+ domainApi->Pulumi.Output.flatMap(api => api.uris->Pulumi.Output.apply(uris => uris.graphQL)),
1578
1604
  )
1579
1605
  Pulumi.Pulumi.export("domainApiRoleArn", domainApiRole->Pulumi.Output.flatMap(role => role.arn))
1580
1606
 
@@ -1587,10 +1613,7 @@ module MakeWithConfig = (
1587
1613
  switch mergedOutputs {
1588
1614
  | Some({domainMerged, platformMerged, domainMergeGate, platformMergeGate}) =>
1589
1615
  let mergeGatedArn = (merged: AppSync_MergedApi.t, gate: Pulumi.Output.t<unit>) =>
1590
- (
1591
- merged.api->Pulumi.Output.flatMap((api: PulumiAws.AppSync.GraphQLApi.t) => api.arn),
1592
- gate,
1593
- )
1616
+ (merged.api->Pulumi.Output.flatMap((api: PulumiAws.AppSync.GraphQLApi.t) => api.arn), gate)
1594
1617
  ->Pulumi.Output.all2
1595
1618
  ->Pulumi.Output.apply(((arn, _)) => arn)
1596
1619
  let mergedEndpoint = (merged: AppSync_MergedApi.t) =>
@@ -1663,13 +1686,15 @@ module MakeWithConfig = (
1663
1686
  | Some({platformMerged}) => platformMerged.api
1664
1687
  | None => platformApi
1665
1688
  }
1666
- let resolvedDomainApiEndpoint = clientDomainApi->Pulumi.Output.flatMap(api =>
1667
- api.uris->Pulumi.Output.apply(uris => uris.graphQL)
1668
- )
1689
+ let resolvedDomainApiEndpoint =
1690
+ clientDomainApi->Pulumi.Output.flatMap(api =>
1691
+ api.uris->Pulumi.Output.apply(uris => uris.graphQL)
1692
+ )
1669
1693
  let resolvedDomainApiRoleArn = domainApiRole->Pulumi.Output.flatMap(role => role.arn)
1670
- let resolvedPlatformApiEndpoint = clientPlatformApi->Pulumi.Output.flatMap(api =>
1671
- api.uris->Pulumi.Output.apply(uris => uris.graphQL)
1672
- )
1694
+ let resolvedPlatformApiEndpoint =
1695
+ clientPlatformApi->Pulumi.Output.flatMap(api =>
1696
+ api.uris->Pulumi.Output.apply(uris => uris.graphQL)
1697
+ )
1673
1698
  let resolvedPlatformApiRoleArn = platformApiRole->Pulumi.Output.flatMap(role => role.arn)
1674
1699
  // Collect admin aggregate + read model resources.
1675
1700
  let adminResourcesOutput =
@@ -1709,7 +1734,13 @@ module MakeWithConfig = (
1709
1734
  adminResourcesOutput,
1710
1735
  )
1711
1736
  ->Pulumi.Output.all5
1712
- ->Pulumi.Output.apply(((domainApiEndpoint, domainApiRoleArn, platformApiEndpoint, platformApiRoleArn, adminResources)) => {
1737
+ ->Pulumi.Output.apply(((
1738
+ domainApiEndpoint,
1739
+ domainApiRoleArn,
1740
+ platformApiEndpoint,
1741
+ platformApiRoleArn,
1742
+ adminResources,
1743
+ )) => {
1713
1744
  let region =
1714
1745
  Pulumi.Config.make(Some("aws"))->Pulumi.Config.get("region")->Option.getOr("unknown")
1715
1746
  ReventlessCore.Plugin_Helpers.firePlatformDeployedHook({
@@ -1753,7 +1784,9 @@ module MakeWithConfig = (
1753
1784
  switch c {
1754
1785
  | ObjectStore({plugin, store}) => Some((plugin, store))
1755
1786
  | Geocoding
1756
- | Messaging => None
1787
+ | Messaging
1788
+ | IdentityProvider =>
1789
+ None
1757
1790
  }
1758
1791
  )
1759
1792
  ->Array.reduce([], (acc, (plugin, store)) =>
@@ -1843,8 +1876,7 @@ module MakeWithConfig = (
1843
1876
  log.info(
1844
1877
  ~comp="Platform:deployPlatform",
1845
1878
  `object store ${plugin}.${store}: never-claimed uploads under ${prefix}/ expire after ` ++
1846
- `${days->Int.toString} days. Claimed objects and objects minted before the claim ` ++
1847
- `component existed carry no pending tag and are outside the rule.`,
1879
+ `${days->Int.toString} days. Claimed objects and objects minted before the claim ` ++ `component existed carry no pending tag and are outside the rule.`,
1848
1880
  )
1849
1881
  })
1850
1882
  )
@@ -1897,8 +1929,9 @@ module MakeWithConfig = (
1897
1929
  ->Dict.get(bucketName)
1898
1930
  ->Option.map(b => {
1899
1931
  ReventlessInfra.Platform.id: bucketName,
1900
- prefixes: declaredStoreServices
1901
- ->Array.filterMap(((_, prefix, bn, _)) => bn == bucketName ? Some(prefix) : None),
1932
+ prefixes: declaredStoreServices->Array.filterMap(
1933
+ ((_, prefix, bn, _)) => bn == bucketName ? Some(prefix) : None,
1934
+ ),
1902
1935
  bucketId: b.bucketId,
1903
1936
  bucketArn: b.bucketArn,
1904
1937
  bucketRegionalDomainName: b.bucketRegionalDomainName,
@@ -1956,7 +1989,7 @@ module MakeWithConfig = (
1956
1989
  let uploadServiceStores =
1957
1990
  declaredStoreServices
1958
1991
  ->Array.map(((qualified, keyPrefix, _logical, physicalBucketName)) => {
1959
- Upload_Presign_S3.qualified: qualified,
1992
+ Upload_Presign_S3.qualified,
1960
1993
  bucketName: physicalBucketName,
1961
1994
  servedPrefix: keyPrefix,
1962
1995
  })
@@ -1993,10 +2026,10 @@ module MakeWithConfig = (
1993
2026
  "objectStores",
1994
2027
  declaredStoreEndpoints
1995
2028
  ->Array.map(e =>
1996
- Pulumi.Output.all2((e.bucketName, e.baseUrl->Pulumi.Output.allOpt))->Pulumi.Output.apply(((
1997
- bucketName,
1998
- baseUrl,
1999
- )) => (e, bucketName, baseUrl))
2029
+ Pulumi.Output.all2((
2030
+ e.bucketName,
2031
+ e.baseUrl->Pulumi.Output.allOpt,
2032
+ ))->Pulumi.Output.apply(((bucketName, baseUrl)) => (e, bucketName, baseUrl))
2000
2033
  )
2001
2034
  ->Pulumi.Output.all
2002
2035
  ->Pulumi.Output.apply(resolved =>
@@ -2053,12 +2086,7 @@ module MakeWithConfig = (
2053
2086
  Util_LocalConfig.get("hostUiProdStacks")
2054
2087
  ->Option.map(Util_HostUiDomain.parseProdStacks)
2055
2088
  ->Option.getOr(Util_HostUiDomain.defaultProdStacks)
2056
- let fqdn = Util_HostUiDomain.deriveFqdn(
2057
- ~baseName,
2058
- ~stack,
2059
- ~baseDomain=bd,
2060
- ~prodStacks,
2061
- )
2089
+ let fqdn = Util_HostUiDomain.deriveFqdn(~baseName, ~stack, ~baseDomain=bd, ~prodStacks)
2062
2090
  Some({Plugin_Stack.fqdn, hostedZoneId: hz})
2063
2091
  | _ => None
2064
2092
  }
@@ -2186,8 +2214,7 @@ module MakeWithConfig = (
2186
2214
  `provisions no place index.\n` ++
2187
2215
  ` Add \`let placeIndex = ReventlessAws.Capability_Geocoding_AwsLocation.make(~name=…)\` ` ++
2188
2216
  `and pass \`~geocoderPlaceIndex=placeIndex\` in \`~hostUiBundle\`.\n` ++
2189
- ` The declaration is generated from the plugins' capabilities.json — regenerate with ` ++
2190
- `\`pnpm run generate:platform\` if it is stale.`,
2217
+ ` The declaration is generated from the plugins' capabilities.json — regenerate with ` ++ `\`pnpm run generate:platform\` if it is stale.`,
2191
2218
  )
2192
2219
  }
2193
2220
 
@@ -2250,8 +2277,7 @@ module MakeWithConfig = (
2250
2277
  `instead — every message is logged and none is sent, and the address is optional.\n` ++
2251
2278
  ` The root must also pass \`~messagingSender\` in \`~hostUiBundle\`, from ` ++
2252
2279
  `\`ReventlessAws.Capability_Messaging.make(~name=…)\`.\n` ++
2253
- ` The declaration is generated from the plugins' capabilities.json — regenerate with ` ++
2254
- `\`pnpm run generate:platform\` if it is stale.`,
2280
+ ` The declaration is generated from the plugins' capabilities.json — regenerate with ` ++ `\`pnpm run generate:platform\` if it is stale.`,
2255
2281
  )
2256
2282
  }
2257
2283
 
@@ -2514,7 +2540,11 @@ module MakeWithConfig = (
2514
2540
  // Expose deploy target via hooks so Plugin_Builder can stamp pluginDefinition.apiTarget.
2515
2541
  // This must be set before P.make() and is captured synchronously by Plugin_Builder
2516
2542
  // (same timing requirement as hooks.api/apiRole).
2517
- hooks.deployTarget := switch apiTarget { | Domain => "Domain" | Platform => "Platform" }
2543
+ hooks.deployTarget :=
2544
+ switch apiTarget {
2545
+ | Domain => "Domain"
2546
+ | Platform => "Platform"
2547
+ }
2518
2548
  // Each plugin stack creates its own scheduler (closures can't cross stacks).
2519
2549
  let scheduler = makeScheduler()
2520
2550
  hooks.scheduler := Some(scheduler)
@@ -2532,8 +2562,9 @@ module MakeWithConfig = (
2532
2562
  // The bucket name comes from the platform stack (deploy the platform first).
2533
2563
  let offloadBucketName: Pulumi.Output.t<string> = switch platformStackRef {
2534
2564
  | Some(stackRef) =>
2535
- (stackRef->Pulumi.StackReference.getOutput("offloadBucket"): Pulumi.Output.t<option<string>>)
2536
- ->Pulumi.Output.apply(o => o->Option.getOr("OFFLOAD_BUCKET_PENDING_PLATFORM_DEPLOY"))
2565
+ (
2566
+ stackRef->Pulumi.StackReference.getOutput("offloadBucket"): Pulumi.Output.t<option<string>>
2567
+ )->Pulumi.Output.apply(o => o->Option.getOr("OFFLOAD_BUCKET_PENDING_PLATFORM_DEPLOY"))
2537
2568
  | None => Pulumi.Output.make("OFFLOAD_BUCKET_PENDING_PLATFORM_DEPLOY")
2538
2569
  }
2539
2570
  // The key this deploy wrote for the plugin's structure, exported below. The
@@ -2600,8 +2631,9 @@ module MakeWithConfig = (
2600
2631
  let geocoderPlaceIndex: Pulumi.Output.t<string> = switch platformStackRef {
2601
2632
  | Some(stackRef) =>
2602
2633
  (
2603
- stackRef->Pulumi.StackReference.getOutput("geocoderPlaceIndex"):
2604
- Pulumi.Output.t<option<string>>
2634
+ stackRef->Pulumi.StackReference.getOutput("geocoderPlaceIndex"): Pulumi.Output.t<
2635
+ option<string>,
2636
+ >
2605
2637
  )->Pulumi.Output.apply(o => o->Option.getOr(""))
2606
2638
  | None => geocoderPlaceIndexRef.contents
2607
2639
  }
@@ -2615,8 +2647,9 @@ module MakeWithConfig = (
2615
2647
  let messagingEmailSender: Pulumi.Output.t<string> = switch platformStackRef {
2616
2648
  | Some(stackRef) =>
2617
2649
  (
2618
- stackRef->Pulumi.StackReference.getOutput("messagingEmailSender"):
2619
- Pulumi.Output.t<option<string>>
2650
+ stackRef->Pulumi.StackReference.getOutput("messagingEmailSender"): Pulumi.Output.t<
2651
+ option<string>,
2652
+ >
2620
2653
  )->Pulumi.Output.apply(o => o->Option.getOr(""))
2621
2654
  | None => messagingEmailSenderRef.contents
2622
2655
  }
@@ -2630,15 +2663,13 @@ module MakeWithConfig = (
2630
2663
  let messagingEmailProvider: Pulumi.Output.t<string> = switch platformStackRef {
2631
2664
  | Some(stackRef) =>
2632
2665
  (
2633
- stackRef->Pulumi.StackReference.getOutput("messagingEmailProvider"):
2634
- Pulumi.Output.t<option<string>>
2666
+ stackRef->Pulumi.StackReference.getOutput("messagingEmailProvider"): Pulumi.Output.t<
2667
+ option<string>,
2668
+ >
2635
2669
  )->Pulumi.Output.apply(o => o->Option.getOr(""))
2636
2670
  | None => messagingEmailProviderRef.contents
2637
2671
  }
2638
- PluginRuntime_Builder.registerCapabilityEnv(
2639
- "MESSAGING_EMAIL_PROVIDER",
2640
- messagingEmailProvider,
2641
- )
2672
+ PluginRuntime_Builder.registerCapabilityEnv("MESSAGING_EMAIL_PROVIDER", messagingEmailProvider)
2642
2673
 
2643
2674
  module P = unpack(plugin)
2644
2675
  let pluginComponent = P.make()
@@ -2687,8 +2718,7 @@ module MakeWithConfig = (
2687
2718
  let defaultOutput: Pulumi.Output.t<option<JSON.t>> =
2688
2719
  stackRef->Pulumi.StackReference.getOutput("default")
2689
2720
  let getMergedExport = (key: string): Pulumi.Output.t<string> => {
2690
- let direct: Pulumi.Output.t<option<string>> =
2691
- stackRef->Pulumi.StackReference.getOutput(key)
2721
+ let direct: Pulumi.Output.t<option<string>> = stackRef->Pulumi.StackReference.getOutput(key)
2692
2722
  (direct, defaultOutput)
2693
2723
  ->Pulumi.Output.all2
2694
2724
  ->Pulumi.Output.apply(((direct, default)) =>
@@ -2778,12 +2808,9 @@ module MakeWithConfig = (
2778
2808
  messagingEmailSender,
2779
2809
  )
2780
2810
  ->Pulumi.Output.all4
2781
- ->Pulumi.Output.apply((((structure, objectStores, placeIndex, emailSender): (
2782
- _,
2783
- option<JSON.t>,
2784
- string,
2785
- string,
2786
- ))) => {
2811
+ ->Pulumi.Output.apply((
2812
+ (structure, objectStores, placeIndex, emailSender): (_, option<JSON.t>, string, string),
2813
+ ) => {
2787
2814
  // ── Declared capabilities ──────────────────────────────────────────
2788
2815
  //
2789
2816
  // A slice that names `Geocoding` in `capabilityNeeds` says its
@@ -2819,14 +2846,16 @@ module MakeWithConfig = (
2819
2846
  ->Array.map(u =>
2820
2847
  switch u.need {
2821
2848
  | Geocoding =>
2822
- `\n Geocoding is \`Capability_Geocoding_AwsLocation.make\`, passed to the ` ++
2823
- `platform as \`~geocoderPlaceIndex\`.`
2849
+ `\n Geocoding is \`Capability_Geocoding_AwsLocation.make\`, passed to the ` ++ `platform as \`~geocoderPlaceIndex\`.`
2824
2850
  | Messaging =>
2825
2851
  `\n Messaging is \`Capability_Messaging.make\`, passed to the platform ` ++
2826
2852
  `as \`~messagingSender\`. The sender itself is configuration and has no ` ++
2827
2853
  `default: set \`platform:messagingEmailSender\` in Pulumi.<stack>.yaml (or ` ++
2828
- `REVENTLESS_MESSAGING_EMAIL_SENDER), then verify the address with SES — or ` ++
2829
- `\`platform:messagingEmailProvider: log\` to log every message and send none.`
2854
+ `REVENTLESS_MESSAGING_EMAIL_SENDER), then verify the address with SES — or ` ++ `\`platform:messagingEmailProvider: log\` to log every message and send none.`
2855
+ | IdentityProvider =>
2856
+ `\n IdentityProvider has no backend on any platform yet — the seam and the ` ++
2857
+ `refusal exist, the Cognito and local implementations do not. A plugin ` ++
2858
+ `declaring this need cannot be deployed until one lands; nothing about this ` ++ `deployment's configuration will change that.`
2830
2859
  }
2831
2860
  )
2832
2861
  // One line per capability, not per declaring component: two slices
@@ -2837,8 +2866,7 @@ module MakeWithConfig = (
2837
2866
  )
2838
2867
  }
2839
2868
 
2840
- let required =
2841
- structure->Option.flatMap(s => s.requiredStores)->Option.getOr([])
2869
+ let required = structure->Option.flatMap(s => s.requiredStores)->Option.getOr([])
2842
2870
  let provisioned =
2843
2871
  objectStores
2844
2872
  ->Option.flatMap(JSON.Decode.object)
@@ -2849,17 +2877,19 @@ module MakeWithConfig = (
2849
2877
  | NotAdopted(missing) =>
2850
2878
  log.warn(
2851
2879
  ~comp="Platform:deployPlugin",
2852
- `declares ${missing->Array.join(", ")} but the platform stack provisions no object stores — ` ++
2853
- `add them to the platform's ~capabilities and redeploy the platform first, ` ++
2854
- `or uploads will fall back to the legacy service and write to the wrong bucket`,
2880
+ `declares ${missing->Array.join(
2881
+ ", ",
2882
+ )} but the platform stack provisions no object stores — ` ++
2883
+ `add them to the platform's ~capabilities and redeploy the platform first, ` ++ `or uploads will fall back to the legacy service and write to the wrong bucket`,
2855
2884
  )
2856
2885
  | Missing({missing, provisioned}) =>
2857
2886
  JsError.throwWithMessage(
2858
- `Plugin requires object store(s) the platform does not provision: ${missing->Array.join(", ")}.\n` ++
2887
+ `Plugin requires object store(s) the platform does not provision: ${missing->Array.join(
2888
+ ", ",
2889
+ )}.\n` ++
2859
2890
  ` The platform stack provisions: ${provisioned->Array.join(", ")}.\n` ++
2860
2891
  ` A store's key is {plugin}.{store}, where {plugin} is the name the plugin registers — ` ++
2861
- `check the capability's spelling and case against it.\n` ++
2862
- ` Add the missing entr(ies) to the platform's ~capabilities and redeploy the platform stack first.`,
2892
+ `check the capability's spelling and case against it.\n` ++ ` Add the missing entr(ies) to the platform's ~capabilities and redeploy the platform stack first.`,
2863
2893
  )
2864
2894
  }
2865
2895
  })
@@ -2870,10 +2900,7 @@ module MakeWithConfig = (
2870
2900
  ->Pulumi.Output.all3
2871
2901
  ->Pulumi.Output.apply(((id, _, _)) => id),
2872
2902
  )
2873
- Pulumi.Pulumi.export(
2874
- "pluginSourceApiId",
2875
- domainApi->Pulumi.Output.flatMap(api => api.id),
2876
- )
2903
+ Pulumi.Pulumi.export("pluginSourceApiId", domainApi->Pulumi.Output.flatMap(api => api.id))
2877
2904
  Pulumi.Pulumi.export(
2878
2905
  "pluginSourceApiEndpoint",
2879
2906
  domainApi->Pulumi.Output.flatMap(api =>
@@ -2926,8 +2953,10 @@ module MakeWithConfig = (
2926
2953
  // re-detect coming to correct it. Waiting on the heartbeat alone gated on a
2927
2954
  // Lambda that has no part in the answer.
2928
2955
  let redetectReady =
2929
- (pluginOutputs.heartbeat, PluginRuntime_Builder.eventCollectorReadyRef.contents)
2930
- ->Pulumi.Output.all2
2956
+ (
2957
+ pluginOutputs.heartbeat,
2958
+ PluginRuntime_Builder.eventCollectorReadyRef.contents,
2959
+ )->Pulumi.Output.all2
2931
2960
  let _ = redetectReady->Pulumi.Output.apply(_ => {
2932
2961
  let hbConfig = PluginRuntime_Builder.heartbeatConfigRef.contents
2933
2962
  switch (Pulumi.Pulumi.isDryRun(), hbConfig.epQueueUrl) {