@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
@@ -59,9 +59,7 @@ let _getUsEast1Provider = (): PulumiAws.Aws.Provider.t =>
59
59
  // `no-cache` here a redeploy uploads the new entry file to S3 while CloudFront
60
60
  // (and browsers) keep serving the previous build: the "deployed but stale" trap.
61
61
  let cacheControlFor = (relativePath: string): string =>
62
- relativePath->String.startsWith("assets/")
63
- ? "public, max-age=31536000, immutable"
64
- : "no-cache"
62
+ relativePath->String.startsWith("assets/") ? "public, max-age=31536000, immutable" : "no-cache"
65
63
 
66
64
  // ── Inline CloudFront SDK binding (deploy-time cache invalidation) ───────────
67
65
  // Mirrors AppSync_Adapter's inline `@aws-sdk` binding pattern. Runs in the
@@ -103,7 +101,10 @@ let invalidateDistribution = async (~distributionId: string, ~paths: array<strin
103
101
  },
104
102
  }
105
103
  let _ = await client->cfSend(input->makeCreateInvalidationCommand)
106
- log.info(~comp="makeUiBundleDistribution", `CloudFront invalidation submitted for ${distributionId}`)
104
+ log.info(
105
+ ~comp="makeUiBundleDistribution",
106
+ `CloudFront invalidation submitted for ${distributionId}`,
107
+ )
107
108
  } catch {
108
109
  | exn =>
109
110
  let msg = exn->JsExn.fromException->Option.flatMap(JsExn.message)->Option.getOr("unknown")
@@ -212,7 +213,9 @@ let makeUiBundleDistribution = (
212
213
  // policy. Always cover `remoteEntry.js` (federation manifest) and, for SPA
213
214
  // shells, `index.html` + `config.json`. Hashed assets fall through to the
214
215
  // CachingOptimized default behavior since their URL changes on every build.
215
- let noCacheBehavior = (pattern: string): PulumiAws.CloudFront.Distribution.orderedCacheBehavior => {
216
+ let noCacheBehavior = (
217
+ pattern: string,
218
+ ): PulumiAws.CloudFront.Distribution.orderedCacheBehavior => {
216
219
  pathPattern: Pulumi.Input.make(pattern),
217
220
  targetOriginId: Pulumi.Input.make(originId),
218
221
  viewerProtocolPolicy: Pulumi.Input.make("redirect-to-https"),
@@ -330,27 +333,27 @@ let makeUiBundleDistribution = (
330
333
  | Some({fqdn}) => Some(Pulumi.Input.make([fqdn]))
331
334
  },
332
335
  origins: (bucket.bucketRegionalDomainName, oac.id)
333
- ->Pulumi.Output.all2
334
- ->Pulumi.Output.apply(((domainName, oacId)) =>
335
- Array.concat(
336
- [
337
- {
338
- PulumiAws.CloudFront.Distribution.domainName: Pulumi.Input.make(domainName),
339
- originId: Pulumi.Input.make(originId),
340
- originAccessControlId: Pulumi.Input.make(oacId),
341
- },
342
- ],
343
- // One S3 origin per served bucket, sharing the same OAC as the bundle
344
- // origin (the OAC only authorizes CloudFront→S3 sigv4 signing; the
345
- // per-bucket read grant is the served bucket's own BucketPolicy below).
346
- servedBuckets->Array.map(sb => {
347
- PulumiAws.CloudFront.Distribution.domainName: sb.bucketRegionalDomainName,
348
- originId: Pulumi.Input.make(servedOriginId(sb.id)),
336
+ ->Pulumi.Output.all2
337
+ ->Pulumi.Output.apply(((domainName, oacId)) =>
338
+ Array.concat(
339
+ [
340
+ {
341
+ PulumiAws.CloudFront.Distribution.domainName: Pulumi.Input.make(domainName),
342
+ originId: Pulumi.Input.make(originId),
349
343
  originAccessControlId: Pulumi.Input.make(oacId),
350
- }),
351
- )
344
+ },
345
+ ],
346
+ // One S3 origin per served bucket, sharing the same OAC as the bundle
347
+ // origin (the OAC only authorizes CloudFront→S3 sigv4 signing; the
348
+ // per-bucket read grant is the served bucket's own BucketPolicy below).
349
+ servedBuckets->Array.map(sb => {
350
+ PulumiAws.CloudFront.Distribution.domainName: sb.bucketRegionalDomainName,
351
+ originId: Pulumi.Input.make(servedOriginId(sb.id)),
352
+ originAccessControlId: Pulumi.Input.make(oacId),
353
+ }),
352
354
  )
353
- ->Pulumi.Output.asInput,
355
+ )
356
+ ->Pulumi.Output.asInput,
354
357
  defaultCacheBehavior: Pulumi.Input.make(
355
358
  (
356
359
  {
@@ -413,27 +416,27 @@ let makeUiBundleDistribution = (
413
416
  ~args={
414
417
  bucket: bucket.id->Pulumi.Output.asInput,
415
418
  policy: (bucket.arn, distribution.arn)
416
- ->Pulumi.Output.all2
417
- ->Pulumi.Output.apply(((bucketArn, distributionArn)) =>
418
- {
419
- "Version": "2012-10-17",
420
- "Statement": [
421
- {
422
- "Sid": "AllowCloudFrontServicePrincipal",
423
- "Effect": "Allow",
424
- "Principal": {"Service": "cloudfront.amazonaws.com"},
425
- "Action": "s3:GetObject",
426
- "Resource": bucketArn ++ "/*",
427
- "Condition": {
428
- "StringEquals": {"AWS:SourceArn": distributionArn},
429
- },
419
+ ->Pulumi.Output.all2
420
+ ->Pulumi.Output.apply(((bucketArn, distributionArn)) =>
421
+ {
422
+ "Version": "2012-10-17",
423
+ "Statement": [
424
+ {
425
+ "Sid": "AllowCloudFrontServicePrincipal",
426
+ "Effect": "Allow",
427
+ "Principal": {"Service": "cloudfront.amazonaws.com"},
428
+ "Action": "s3:GetObject",
429
+ "Resource": bucketArn ++ "/*",
430
+ "Condition": {
431
+ "StringEquals": {"AWS:SourceArn": distributionArn},
430
432
  },
431
- ],
432
- }
433
- ->JSON.stringifyAny
434
- ->Option.getUnsafe
435
- )
436
- ->Pulumi.Output.asInput,
433
+ },
434
+ ],
435
+ }
436
+ ->JSON.stringifyAny
437
+ ->Option.getUnsafe
438
+ )
439
+ ->Pulumi.Output.asInput,
437
440
  },
438
441
  )
439
442
 
@@ -461,27 +464,27 @@ let makeUiBundleDistribution = (
461
464
  ~args={
462
465
  bucket: sb.bucketId,
463
466
  policy: (sb.bucketArn->Pulumi.Output.fromInput, distribution.arn)
464
- ->Pulumi.Output.all2
465
- ->Pulumi.Output.apply(((bucketArn, distributionArn)) =>
466
- {
467
- "Version": "2012-10-17",
468
- "Statement": [
469
- {
470
- "Sid": "AllowCloudFrontServicePrincipal",
471
- "Effect": "Allow",
472
- "Principal": {"Service": "cloudfront.amazonaws.com"},
473
- "Action": "s3:GetObject",
474
- "Resource": bucketArn ++ "/*",
475
- "Condition": {
476
- "StringEquals": {"AWS:SourceArn": distributionArn},
477
- },
467
+ ->Pulumi.Output.all2
468
+ ->Pulumi.Output.apply(((bucketArn, distributionArn)) =>
469
+ {
470
+ "Version": "2012-10-17",
471
+ "Statement": [
472
+ {
473
+ "Sid": "AllowCloudFrontServicePrincipal",
474
+ "Effect": "Allow",
475
+ "Principal": {"Service": "cloudfront.amazonaws.com"},
476
+ "Action": "s3:GetObject",
477
+ "Resource": bucketArn ++ "/*",
478
+ "Condition": {
479
+ "StringEquals": {"AWS:SourceArn": distributionArn},
478
480
  },
479
- ],
480
- }
481
- ->JSON.stringifyAny
482
- ->Option.getUnsafe
483
- )
484
- ->Pulumi.Output.asInput,
481
+ },
482
+ ],
483
+ }
484
+ ->JSON.stringifyAny
485
+ ->Option.getUnsafe
486
+ )
487
+ ->Pulumi.Output.asInput,
485
488
  },
486
489
  )
487
490
  })
@@ -491,9 +494,7 @@ let makeUiBundleDistribution = (
491
494
  | Some(dir) =>
492
495
  let allEntries = Util.StaticBundle.walk(dir)
493
496
  if allEntries->Array.length == 0 {
494
- JsError.throwWithMessage(
495
- `Plugin_Stack.makeUiBundleDistribution: assetsDir is empty: ${dir}`,
496
- )
497
+ JsError.throwWithMessage(`Plugin_Stack.makeUiBundleDistribution: assetsDir is empty: ${dir}`)
497
498
  }
498
499
  // Drop excluded relative paths before BucketObject creation. Used so the
499
500
  // host-shell deploy can skip the dev-mode `public/config.json` it ships
@@ -502,21 +503,20 @@ let makeUiBundleDistribution = (
502
503
  // upload (whichever Pulumi applies last wins).
503
504
  let entries =
504
505
  allEntries->Array.filter(entry => !(excludeFiles->Array.includes(entry.relativePath)))
505
- let objectEtags =
506
- entries->Array.map(entry => {
507
- let obj = PulumiAws.S3.BucketObject.make(
508
- ~name=name ++ "-asset-" ++ Util.StaticBundle.sanitizeName(entry.relativePath),
509
- ~args={
510
- bucket: bucket.id->Pulumi.Output.asInput,
511
- key: Pulumi.Input.make(entry.relativePath),
512
- source: Pulumi.Input.make(entry.fileAsset),
513
- contentType: Pulumi.Input.make(Util.StaticBundle.contentTypeFor(entry.relativePath)),
514
- cacheControl: Pulumi.Input.make(cacheControlFor(entry.relativePath)),
515
- etag: Pulumi.Input.make(entry.contentHash),
516
- },
517
- )
518
- obj.etag
519
- })
506
+ let objectEtags = entries->Array.map(entry => {
507
+ let obj = PulumiAws.S3.BucketObject.make(
508
+ ~name=name ++ "-asset-" ++ Util.StaticBundle.sanitizeName(entry.relativePath),
509
+ ~args={
510
+ bucket: bucket.id->Pulumi.Output.asInput,
511
+ key: Pulumi.Input.make(entry.relativePath),
512
+ source: Pulumi.Input.make(entry.fileAsset),
513
+ contentType: Pulumi.Input.make(Util.StaticBundle.contentTypeFor(entry.relativePath)),
514
+ cacheControl: Pulumi.Input.make(cacheControlFor(entry.relativePath)),
515
+ etag: Pulumi.Input.make(entry.contentHash),
516
+ },
517
+ )
518
+ obj.etag
519
+ })
520
520
 
521
521
  // Auto-invalidate CloudFront once every object is (re)uploaded. Stable-named
522
522
  // entry files (index.html, the module-federation mf-entry-bootstrap-*.js)
@@ -629,7 +629,8 @@ let makeServedBucketDistribution = (
629
629
  | None => None
630
630
  | Some({fqdn}) => Some(Pulumi.Input.make([fqdn]))
631
631
  },
632
- origins: oac.id->Pulumi.Output.apply(oacId =>
632
+ origins: oac.id
633
+ ->Pulumi.Output.apply(oacId =>
633
634
  servedBuckets->Array.map(sb => {
634
635
  PulumiAws.CloudFront.Distribution.domainName: sb.bucketRegionalDomainName,
635
636
  originId: Pulumi.Input.make(originIdFor(sb.id)),
@@ -710,27 +711,27 @@ let makeServedBucketDistribution = (
710
711
  ~args={
711
712
  bucket: sb.bucketId,
712
713
  policy: (sb.bucketArn->Pulumi.Output.fromInput, distribution.arn)
713
- ->Pulumi.Output.all2
714
- ->Pulumi.Output.apply(((bucketArn, distributionArn)) =>
715
- {
716
- "Version": "2012-10-17",
717
- "Statement": [
718
- {
719
- "Sid": "AllowCloudFrontServicePrincipal",
720
- "Effect": "Allow",
721
- "Principal": {"Service": "cloudfront.amazonaws.com"},
722
- "Action": "s3:GetObject",
723
- "Resource": bucketArn ++ "/*",
724
- "Condition": {
725
- "StringEquals": {"AWS:SourceArn": distributionArn},
726
- },
714
+ ->Pulumi.Output.all2
715
+ ->Pulumi.Output.apply(((bucketArn, distributionArn)) =>
716
+ {
717
+ "Version": "2012-10-17",
718
+ "Statement": [
719
+ {
720
+ "Sid": "AllowCloudFrontServicePrincipal",
721
+ "Effect": "Allow",
722
+ "Principal": {"Service": "cloudfront.amazonaws.com"},
723
+ "Action": "s3:GetObject",
724
+ "Resource": bucketArn ++ "/*",
725
+ "Condition": {
726
+ "StringEquals": {"AWS:SourceArn": distributionArn},
727
727
  },
728
- ],
729
- }
730
- ->JSON.stringifyAny
731
- ->Option.getUnsafe
732
- )
733
- ->Pulumi.Output.asInput,
728
+ },
729
+ ],
730
+ }
731
+ ->JSON.stringifyAny
732
+ ->Option.getUnsafe
733
+ )
734
+ ->Pulumi.Output.asInput,
734
735
  },
735
736
  )
736
737
  })
@@ -30,7 +30,8 @@ type t = {
30
30
  a slow deploy or a throttle cannot trip it, close enough that a stopped
31
31
  scheduler is caught within the hour rather than by its consequences days
32
32
  later. Deployments that set a long `heartbeatInterval` should raise it. */
33
- let defaultSilenceWindowSeconds = 60 * 60
33
+ let defaultSilenceWindowSeconds =
34
+ 60 * 60
34
35
 
35
36
  let errorsAlarm = {
36
37
  metricName: "Errors",
@@ -11,7 +11,10 @@ let toResource: Types.AppSync.resolver => ReventlessInfra.Adapter.resource = ({
11
11
  ~service=id->Pulumi.Output.apply(_ => AWS.AppSync.service),
12
12
  ~resourceInfo=(type_, field)
13
13
  ->Pulumi.Output.all2
14
- ->Pulumi.Output.apply(((type_, field)) => ReventlessInfra.Adapter.ApiResolver({typeName: type_, fieldName: field})),
14
+ ->Pulumi.Output.apply(((type_, field)) => ReventlessInfra.Adapter.ApiResolver({
15
+ typeName: type_,
16
+ fieldName: field,
17
+ })),
15
18
  ~resourceType="aws:appsync:Resolver"->Pulumi.Output.make,
16
19
  )
17
20
 
@@ -31,6 +34,9 @@ let toResourceNative: PulumiAws.AwsNative.AppSync.Resolver.t => ReventlessInfra.
31
34
  ~service=id->Pulumi.Output.apply(_ => AWS.AppSync.service),
32
35
  ~resourceInfo=(typeName, fieldName)
33
36
  ->Pulumi.Output.all2
34
- ->Pulumi.Output.apply(((t, f)) => ReventlessInfra.Adapter.ApiResolver({typeName: t, fieldName: f})),
37
+ ->Pulumi.Output.apply(((t, f)) => ReventlessInfra.Adapter.ApiResolver({
38
+ typeName: t,
39
+ fieldName: f,
40
+ })),
35
41
  ~resourceType="aws-native:appsync:Resolver"->Pulumi.Output.make,
36
42
  )
@@ -21,7 +21,7 @@ type awsCredentials = {
21
21
  // defaultProvider() returns a credential-provider function.
22
22
  // It resolves credentials from env vars → SSO → ini files → IMDSv2.
23
23
  @module("@aws-sdk/credential-provider-node")
24
- external defaultProvider: unit => (unit => promise<awsCredentials>) = "defaultProvider"
24
+ external defaultProvider: unit => unit => promise<awsCredentials> = "defaultProvider"
25
25
 
26
26
  // ── SigV4 signer ──────────────────────────────────────────────────────────────
27
27
 
@@ -139,10 +139,12 @@ let buildQuery = (
139
139
 
140
140
  // Signs and sends a GraphQL query; returns the `data` object or None on error.
141
141
  // ~queryString must be a complete "query { field(...) { ... } }" string.
142
- let sendQuery = async (~endpoint: string, ~region: string, ~queryString: string): option<JSON.t> => {
142
+ let sendQuery = async (~endpoint: string, ~region: string, ~queryString: string): option<
143
+ JSON.t,
144
+ > => {
143
145
  let url = parseUrl(endpoint)
144
- let hostname: string = (url)["hostname"]
145
- let path: string = (url)["pathname"]
146
+ let hostname: string = url["hostname"]
147
+ let path: string = url["pathname"]
146
148
 
147
149
  let body =
148
150
  Dict.fromArray([("query", queryString->JSON.Encode.string)])
@@ -186,19 +188,18 @@ let sendMutation = async (
186
188
  ~variables: 'a,
187
189
  ) => {
188
190
  let url = parseUrl(endpoint)
189
- let hostname: string = (url)["hostname"]
190
- let path: string = (url)["pathname"]
191
+ let hostname: string = url["hostname"]
192
+ let path: string = url["pathname"]
191
193
 
192
194
  // Normalise variables to a JSON.t dict (strips undefined optional fields)
193
- let variablesDict: dict<JSON.t> =
194
- switch variables->JSON.stringifyAny {
195
- | Some(str) =>
196
- switch str->JSON.parseOrThrow {
197
- | Object(d) => d
198
- | _ => Dict.make()
199
- }
200
- | None => Dict.make()
195
+ let variablesDict: dict<JSON.t> = switch variables->JSON.stringifyAny {
196
+ | Some(str) =>
197
+ switch str->JSON.parseOrThrow {
198
+ | Object(d) => d
199
+ | _ => Dict.make()
201
200
  }
201
+ | None => Dict.make()
202
+ }
202
203
 
203
204
  let query = buildQuery(~mutation, ~selection, ~variablesDict)
204
205
  let body =
@@ -92,8 +92,9 @@ let resolvePackageRoot = (~fromPulumiProject: bool=false, packageName: string):
92
92
  let viaFramework = () => NodePath.dirname(localRequire->NodeModule.requireResolve(request))
93
93
  let viaPulumiProject = () =>
94
94
  NodePath.dirname(
95
- NodeModule.createRequire(NodeProcess.cwd() ++ "/index.js")
96
- ->NodeModule.requireResolve(request),
95
+ NodeModule.createRequire(NodeProcess.cwd() ++ "/index.js")->NodeModule.requireResolve(
96
+ request,
97
+ ),
97
98
  )
98
99
  let (preferred, fallback) = fromPulumiProject
99
100
  ? (viaPulumiProject, viaFramework)
@@ -204,7 +205,11 @@ let rec walkDir = (
204
205
  let newPrefix = prefix == "" ? entryName : prefix ++ "/" ++ entryName
205
206
  walkDir(~dir=NodePath.join([dir, entryName]), ~prefix=newPrefix, ~assets, ~paths)
206
207
  }
207
- } else if entryName == "package.json" || entryName->String.endsWith(".mjs") || entryName->String.endsWith(".js") {
208
+ } else if (
209
+ entryName == "package.json" ||
210
+ entryName->String.endsWith(".mjs") ||
211
+ entryName->String.endsWith(".js")
212
+ ) {
208
213
  let relPath = prefix == "" ? entryName : prefix ++ "/" ++ entryName
209
214
  let absPath = NodePath.join([dir, entryName])
210
215
  assets->Dict.set(
@@ -339,42 +344,42 @@ let isRuntimeProvided = (specifier: string, ~pkgName: string): bool =>
339
344
  nodeBuiltins->Set.has(specifier) ||
340
345
  nodeBuiltins->Set.has(pkgName) ||
341
346
  pkgName->String.startsWith("@aws-sdk/") ||
342
- pkgName->String.startsWith("@smithy/") ||
343
- (
344
- try {
345
- let _ = resolvePackageRoot(pkgName)
346
- true
347
- } catch {
348
- | _ => false
349
- }
350
- )
351
-
352
- // Memo for isFrameworkPackage — one require.resolve per package name per deploy.
353
- let frameworkPackageCache: dict<bool> = Dict.make()
347
+ pkgName->String.startsWith("@smithy/") || try {
348
+ let _ = resolvePackageRoot(pkgName)
349
+ true
350
+ } catch {
351
+ | _ => false
352
+ }
354
353
 
355
- /**
356
- * Whether the framework's own module resolution reaches this package: the
357
- * deploy-time stand-in for "the Lambda layer already carries it", since the
358
- * layer is built from reventless-aws's dependency closure.
359
- *
360
- * Deliberately framework-rooted only — no Pulumi-project fallback. The question
361
- * here is the opposite of `resolvePackageRoot`'s: a package the *project*
362
- * resolves but the framework does not is precisely a user package, which is
363
- * what the closure walk below must treat as a starting point rather than as
364
- * something the runtime provides.
365
- */
366
- let isFrameworkPackage = (pkgName: string): bool =>
367
- switch frameworkPackageCache->Dict.get(pkgName) {
368
- | Some(known) => known
369
- | None =>
370
- let known = try {
371
- let _ = localRequire->NodeModule.requireResolve(pkgName ++ "/package.json")
372
- true
373
- } catch {
374
- | _ => false
354
+ /** The framework's own package root: this module ships inside reventless-aws, so
355
+ the nearest package.json walking up from it is reventless-aws's own. */
356
+ let frameworkPackageRoot: string = {
357
+ let filePath = newURL(%raw("import.meta.url"))->pathname
358
+ let dirRef = ref(NodePath.dirname(filePath))
359
+ let foundRef: ref<option<string>> = ref(None)
360
+ while dirRef.contents != "/" && foundRef.contents->Option.isNone {
361
+ if NodeFs.existsSync(NodePath.join([dirRef.contents, "package.json"])) {
362
+ foundRef := Some(dirRef.contents)
363
+ } else {
364
+ dirRef := NodePath.dirname(dirRef.contents)
375
365
  }
376
- frameworkPackageCache->Dict.set(pkgName, known)
377
- known
366
+ }
367
+ foundRef.contents->Option.getOr(NodePath.dirname(filePath))
368
+ }
369
+
370
+ /** The `dependencies` a package declares — the edges the layer's own npm install
371
+ follows. devDependencies are deliberately not read: the layer is a production
372
+ install, so a dev-only package is not in it. */
373
+ let declaredDependencies = (packageRoot: string): array<string> =>
374
+ try {
375
+ NodeFs.readFileSync(NodePath.join([packageRoot, "package.json"]))
376
+ ->JSON.parseOrThrow
377
+ ->JSON.Decode.object
378
+ ->Option.flatMap(o => o->Dict.get("dependencies"))
379
+ ->Option.flatMap(JSON.Decode.object)
380
+ ->Option.mapOr([], Dict.keysToArray)
381
+ } catch {
382
+ | _ => []
378
383
  }
379
384
 
380
385
  // The distinct bare packages a package's own bundled files import, memoised per
@@ -419,14 +424,85 @@ let resolvePackageRootFrom = (~fromRoot: string, pkgName: string): option<string
419
424
  try {
420
425
  Some(
421
426
  NodePath.dirname(
422
- NodeModule.createRequire(NodePath.join([fromRoot, "index.js"]))
423
- ->NodeModule.requireResolve(pkgName ++ "/package.json"),
427
+ NodeModule.createRequire(NodePath.join([fromRoot, "index.js"]))->NodeModule.requireResolve(
428
+ pkgName ++ "/package.json",
429
+ ),
424
430
  ),
425
431
  )
426
432
  } catch {
427
433
  | _ => None
428
434
  }
429
435
 
436
+ /** Packages the layer carries that reventless-aws does not declare, mirroring
437
+ `includeModules` in the layer builder's config. `@rescript/runtime` is the
438
+ case that matters: it is a transitive of `rescript`, which the layer excludes
439
+ as a build tool, so the production closure misses it while the layer holds
440
+ it. Without this the walk would add 19 MB to every archive.
441
+
442
+ The layer's `excludeModules` are deliberately NOT mirrored. Those are
443
+ deploy-time-only packages (the @pulumi bindings, esbuild, the SSH stack); the
444
+ walk is import-driven, so a package no runtime file imports never comes up. */
445
+ let layerIncludedModules = Set.fromArray(["@rescript/runtime"])
446
+
447
+ // Memo for the framework's dependency closure — one walk per deploy.
448
+ let frameworkClosureCache: ref<option<dict<bool>>> = ref(None)
449
+
450
+ /**
451
+ * The packages the Lambda layer carries: reventless-aws and its transitive
452
+ * `dependencies`, which is exactly what the layer builder npm-installs.
453
+ *
454
+ * Membership rather than resolvability, and the distinction is the whole point.
455
+ * Resolvability asked whether `require.resolve` reaches a package from this
456
+ * module — but Node resolution walks *up*, so in a consumer repo with a hoisted
457
+ * `node_modules` every package at the workspace root answers yes, including the
458
+ * plugin packages and their traits. That made this predicate true for packages
459
+ * the layer has never held: they were dropped from the archive as
460
+ * layer-provided, and the plugin package itself was filtered out of the closure
461
+ * walk's starting points, so its imports were never walked at all. The failure
462
+ * lands at the first command as `Cannot find package`, on a green deploy.
463
+ *
464
+ * Declared edges are followed from the declaring package's own directory, so a
465
+ * pnpm layout resolves the same way the runtime would. A dependency that is
466
+ * declared but not installed still counts as the layer's: the archive is not the
467
+ * place to compensate for an incomplete install.
468
+ */
469
+ let frameworkPackages = (): dict<bool> =>
470
+ switch frameworkClosureCache.contents {
471
+ | Some(closure) => closure
472
+ | None =>
473
+ let closure = Dict.make()
474
+ let pending = [frameworkPackageRoot]
475
+ let next = ref(0)
476
+ while next.contents < pending->Array.length {
477
+ let root = pending->Array.getUnsafe(next.contents)
478
+ next := next.contents + 1
479
+ declaredDependencies(root)->Array.forEach(dep =>
480
+ if !(closure->Dict.has(dep)) {
481
+ closure->Dict.set(dep, true)
482
+ switch resolvePackageRootFrom(~fromRoot=root, dep) {
483
+ | Some(depRoot) => pending->Array.push(depRoot)
484
+ | None => ()
485
+ }
486
+ }
487
+ )
488
+ }
489
+ frameworkClosureCache := Some(closure)
490
+ closure
491
+ }
492
+
493
+ /**
494
+ * Whether the deployed runtime already carries this package, so the archive
495
+ * need not: reventless-aws itself, or anything in its dependency closure.
496
+ *
497
+ * A package the *project* resolves but this does not is precisely a user
498
+ * package, which the closure walk below must treat as a starting point rather
499
+ * than as something the runtime provides.
500
+ */
501
+ let isFrameworkPackage = (pkgName: string): bool =>
502
+ pkgName == "@reventlessdev/reventless-aws" ||
503
+ layerIncludedModules->Set.has(pkgName) ||
504
+ frameworkPackages()->Dict.has(pkgName)
505
+
430
506
  /**
431
507
  * Carry every package a bundled *user* package imports into the archive, and
432
508
  * every package those import in turn.
@@ -496,15 +572,16 @@ let assertRuntimeExtensionImportsResolvable = (
496
572
  if relPath->String.endsWith(".mjs") || relPath->String.endsWith(".js") {
497
573
  NodeFs.readFileSync(absPath)
498
574
  ->staticImportSpecifiers
499
- ->Array.forEach(specifier =>
500
- if isBareSpecifier(specifier) {
501
- let dep = extractPackageName(specifier)
502
- if !(bundledPackages->Dict.has(dep)) && !isRuntimeProvided(specifier, ~pkgName=dep) {
503
- JsError.throwWithMessage(
504
- `runtime extension package "${pkgName}" imports "${specifier}" (${relPath}), which is neither bundled into the code archive nor provided in the deployed runtime. The extension would be skipped at every cold start with "could not be loaded". Declare the package in the extension's companionModuleUrls — the import.meta.url of one of its modules — so it rides into the archive alongside the extension.`,
505
- )
506
- }
507
- }
575
+ ->Array.forEach(
576
+ specifier =>
577
+ if isBareSpecifier(specifier) {
578
+ let dep = extractPackageName(specifier)
579
+ if !(bundledPackages->Dict.has(dep)) && !isRuntimeProvided(specifier, ~pkgName=dep) {
580
+ JsError.throwWithMessage(
581
+ `runtime extension package "${pkgName}" imports "${specifier}" (${relPath}), which is neither bundled into the code archive nor provided in the deployed runtime. The extension would be skipped at every cold start with "could not be loaded". Declare the package in the extension's companionModuleUrls — the import.meta.url of one of its modules — so it rides into the archive alongside the extension.`,
582
+ )
583
+ }
584
+ },
508
585
  )
509
586
  }
510
587
  )
@@ -554,15 +631,16 @@ let buildCodeArchive = (
554
631
  )
555
632
  })
556
633
  // Co-bundle effect whenever reventless-aws is bundled (see comment above).
557
- let allPackageDirs =
558
- if packageDirs->Dict.has("@reventlessdev/reventless-aws") &&
559
- !(packageDirs->Dict.has("effect")) {
560
- let dirs = packageDirs->Dict.copy
561
- dirs->Dict.set("effect", resolvePackageRoot("effect"))
562
- dirs
563
- } else {
564
- packageDirs
565
- }
634
+ let allPackageDirs = if (
635
+ packageDirs->Dict.has("@reventlessdev/reventless-aws") && !(packageDirs->Dict.has("effect"))
636
+ ) {
637
+ let dirs = packageDirs->Dict.copy
638
+ dirs->Dict.set("effect", resolvePackageRoot("effect"))
639
+ dirs
640
+ } else {
641
+ packageDirs
642
+ }
643
+
566
644
  // Registered runtime extensions ride along (with their declared companion
567
645
  // packages), so the entry shell can import them at cold start. No-op when
568
646
  // nothing is registered — the archive, and therefore `sourceCodeHash`, is
@@ -598,15 +676,14 @@ let buildCodeArchive = (
598
676
  })
599
677
  let code = Pulumi.Archive.assetArchive(archiveContents)
600
678
  packageContentHashes.contents->Array.sort(String.compare)
601
- let sourceCodeHash =
602
- hashString(
603
- reExportCode ++
604
- "\n---\n" ++
605
- packageContentHashes.contents->Array.join(",") ++
606
- "\n---\n" ++
607
- extraHashEntries->Array.join("\n") ++
608
- "\n---\n" ++
609
- loaderHash,
610
- )
679
+ let sourceCodeHash = hashString(
680
+ reExportCode ++
681
+ "\n---\n" ++
682
+ packageContentHashes.contents->Array.join(",") ++
683
+ "\n---\n" ++
684
+ extraHashEntries->Array.join("\n") ++
685
+ "\n---\n" ++
686
+ loaderHash,
687
+ )
611
688
  {code, sourceCodeHash}
612
689
  }