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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (232) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/package.json +11 -11
  3. package/scripts/ProvisionIdentity.res +8 -3
  4. package/src/Platform.res +327 -328
  5. package/src/Platform.res.mjs +4 -2
  6. package/src/Platform_Casts.res +13 -4
  7. package/src/adapter/Api/AppSyncEventsSigner_Ops.res +8 -4
  8. package/src/adapter/Api/AppSync_EventsApi.res +12 -14
  9. package/src/adapter/Api/AppSync_Resolver_Native.res +3 -4
  10. package/src/adapter/Api/AppSync_Resolver_Retrying.res +78 -70
  11. package/src/adapter/Api/AppSync_SourceApiAssociation_Retrying.res +11 -8
  12. package/src/adapter/Api/Platform_AdminScan_Ops.res +15 -14
  13. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res +10 -2
  14. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res +40 -50
  15. package/src/adapter/Api/Platform_UIFragments_Lambda.res +39 -32
  16. package/src/adapter/Api/Platform_UIFragments_Lambda_Ops.res +5 -2
  17. package/src/adapter/Auth/Auth_ActiveRolePoolAttachment.res +0 -1
  18. package/src/adapter/Auth/Auth_ActiveRoleStore.res +1 -2
  19. package/src/adapter/Auth/Auth_ActiveRoleStore_Schema.res +1 -4
  20. package/src/adapter/Auth/Auth_ActiveRoleTrigger_Ops.res +5 -2
  21. package/src/adapter/Auth/Auth_Cognito.res +3 -9
  22. package/src/adapter/CommandGenerator/CommandGeneratorResolvers_AppSync.res +1 -4
  23. package/src/adapter/CommandGenerator/CommandGeneratorResolvers_AppSync_NoOp.res +3 -1
  24. package/src/adapter/CommandGenerator/InboundTranslationResolvers_AppSync.res +1 -4
  25. package/src/adapter/CommandTopic/CommandTopicChannel_SQS.res +7 -4
  26. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Async.res +6 -1
  27. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_FIFO.res +7 -4
  28. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Runtime.res +24 -18
  29. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Sync.res +9 -4
  30. package/src/adapter/CommandTopic/CommandTopicRegistry.res +1 -4
  31. package/src/adapter/Counter/CounterHandler_DynamoDbStream.res +15 -15
  32. package/src/adapter/DcbEventLog/DcbEventLogStorage_DynamoDb.res +9 -5
  33. package/src/adapter/DcbEventLog/DcbEventLogStorage_DynamoDb_Runtime.res +179 -213
  34. package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream.res +5 -5
  35. package/src/adapter/EventCollector/EventCollectorChannel_Helpers.res +6 -2
  36. package/src/adapter/EventCollector/EventCollectorChannel_SQS.res +14 -7
  37. package/src/adapter/EventCollector/EventCollectorChannel_SQS_FIFO.res +14 -7
  38. package/src/adapter/EventCollector/EventCollectorChannel_SQS_Runtime.res +6 -3
  39. package/src/adapter/EventLog/EventLogStorage_DynamoDbStream.res +6 -1
  40. package/src/adapter/EventLog/EventLogStorage_DynamoDb_Runtime.res +13 -17
  41. package/src/adapter/EventLog/EventLogStorage_Postgres.res +7 -4
  42. package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res +12 -7
  43. package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync_Ops.res +4 -2
  44. package/src/adapter/EventTopic/EventTopicPublisher_DynamoDbStream.res +3 -2
  45. package/src/adapter/EventTopic/EventTopicPublisher_SNS.res +20 -12
  46. package/src/adapter/EventTopic/EventTopicPublisher_SNS_FIFO.res +21 -9
  47. package/src/adapter/EventTopic/EventTopicPublisher_SNS_Runtime.res +5 -3
  48. package/src/adapter/Geocoder/Geocoder_AwsLocation_Resolver.res +6 -3
  49. package/src/adapter/Mcp/MCP_Lambda.res +31 -46
  50. package/src/adapter/Messaging/Messaging_Ses_Backend.res +11 -7
  51. package/src/adapter/Messaging/Messaging_Ses_Backend.res.mjs +2 -4
  52. package/src/adapter/Monitoring/Monitoring_CloudWatch.res +16 -16
  53. package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res +5 -10
  54. package/src/adapter/Postgres/PgChangeFeedRelay_Runtime.res +4 -2
  55. package/src/adapter/Postgres/PgConnection.res +6 -5
  56. package/src/adapter/Postgres/PgMigration_Builder.res +4 -8
  57. package/src/adapter/Postgres/PgRuntime.res +4 -7
  58. package/src/adapter/QueryDb/PgQueryResolver_Builder.res +9 -11
  59. package/src/adapter/QueryDb/PgQueryResolver_Lambda.res +22 -21
  60. package/src/adapter/QueryDb/QueryDbResolvers_AppSync.res +114 -111
  61. package/src/adapter/QueryDb/QueryDbResolvers_Lambda.res +1 -7
  62. package/src/adapter/QueryDb/QueryDbStorage.res +4 -2
  63. package/src/adapter/QueryDb/QueryDbStorage_DynamoDb.res +23 -24
  64. package/src/adapter/QueryDb/QueryDbStorage_DynamoDbStream.res +8 -2
  65. package/src/adapter/QueryDb/QueryDbStorage_DynamoDb_Runtime.res +71 -73
  66. package/src/adapter/QueryDb/QueryDbStorage_Postgres.res +5 -2
  67. package/src/adapter/QueryDb/QueryInterceptor_Provisioning.res +1 -4
  68. package/src/adapter/QueryEngine/QueryEngine_DynamoDb.res +8 -2
  69. package/src/adapter/Runtime/AggregateRuntime_Builder_Micro.res +73 -81
  70. package/src/adapter/Runtime/AggregateRuntime_Builder_Micro_Async.res +72 -78
  71. package/src/adapter/Runtime/AggregateRuntime_Builder_PerAggregate.res +44 -67
  72. package/src/adapter/Runtime/AggregateRuntime_Builder_PerAggregate_Async.res +44 -67
  73. package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res +53 -82
  74. package/src/adapter/Runtime/AggregateRuntime_Builder_Single_Async.res +47 -77
  75. package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res +18 -11
  76. package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res +80 -97
  77. package/src/adapter/Runtime/CounterEntryPoint_Ops.res +2 -7
  78. package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res +16 -10
  79. package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res +24 -15
  80. package/src/adapter/Runtime/EventCollectorRuntime_Builder_PerEventCollector.res +17 -37
  81. package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res +31 -57
  82. package/src/adapter/Runtime/EventMapperEntryPoint_Ops.res +18 -24
  83. package/src/adapter/Runtime/ExtensionPointEntryPoint_Ops.res +1 -5
  84. package/src/adapter/Runtime/ExtensionPointRuntime_Builder_PerExtensionPoint.res +4 -3
  85. package/src/adapter/Runtime/HeartbeatEntryPoint.res +0 -1
  86. package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.res +4 -2
  87. package/src/adapter/Runtime/PgMigrationEntryPoint.res +0 -1
  88. package/src/adapter/Runtime/PgQueryResolverEntryPoint_Ops.res +3 -2
  89. package/src/adapter/Runtime/PluginExtensionPointEntryPoint.res +1 -2
  90. package/src/adapter/Runtime/ProjectionEntryPoint_Ops.res +4 -5
  91. package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res +22 -10
  92. package/src/adapter/Runtime/RuntimeExtensionEntryPoint_Ops.res +8 -2
  93. package/src/adapter/Runtime/SideEffectHandlerRuntime_Builder_Single.res +14 -31
  94. package/src/adapter/Runtime/StateChangeSliceRuntime_Builder_Single.res +16 -17
  95. package/src/adapter/Runtime/StateViewSliceEntryPoint_Ops.res +10 -9
  96. package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res +46 -52
  97. package/src/adapter/Runtime/StreamRoutedEntryPoint_Ops.res +36 -41
  98. package/src/adapter/Runtime/TaskBucketEntryPoint_Ops.res +31 -37
  99. package/src/adapter/Runtime/TaskRuntime_Builder_PerBucket.res +6 -7
  100. package/src/adapter/ScheduledPublisher/ScheduledPublisher_CloudWatchEvents.res +4 -6
  101. package/src/adapter/ScheduledPublisher/ScheduledPublisher_CloudWatchEvents_Runtime.res +2 -6
  102. package/src/adapter/StateTopic/StateTopic_AppSync.res +21 -16
  103. package/src/adapter/StateTopic/StateTopic_AppSync_Ops.res +36 -32
  104. package/src/adapter/Task/TaskBucket_S3.res +6 -1
  105. package/src/adapter/Upload/Upload_Claim_S3.res +17 -20
  106. package/src/adapter/Upload/Upload_Claim_S3_Ops.res +5 -2
  107. package/src/adapter/Upload/Upload_Presign_S3.res +51 -49
  108. package/src/capability/Capability_Geocoding_AwsLocation.res +4 -1
  109. package/src/capability/Capability_Messaging.res +4 -5
  110. package/src/capability/Capability_Messaging_Ses.res +3 -1
  111. package/src/capability/Capability_ObjectStore_S3.res +1 -7
  112. package/src/components/Api/AppSync_Adapter.res +28 -41
  113. package/src/components/Api/AppSync_SdlDecorate.res +38 -30
  114. package/src/components/AutomationSlice_Builder.res +6 -4
  115. package/src/components/ExtensionPoint_Builder.res +9 -10
  116. package/src/components/InboundTranslationSlice_Builder.res +7 -8
  117. package/src/components/OutboundTranslationSlice_Builder.res +14 -8
  118. package/src/components/OutboundTranslationSlice_Builder.res.mjs +1 -1
  119. package/src/components/SideEffectHandler_Single.res +4 -4
  120. package/src/components/StateViewSlice_Builder.res +6 -4
  121. package/src/components/StateViewSlice_Builder_Stream.res +6 -4
  122. package/src/components/Task_Builder_PerBucket.res +10 -11
  123. package/src/plugin/cloner/ClonerRunner_Fargate.res +3 -10
  124. package/src/plugin/cloner/ClonerRunner_Fargate_Runtime.res +1 -4
  125. package/src/plugin/heartbeat/HeartbeatRunner_CloudWatchEvents.res +5 -5
  126. package/src/plugin/runtime/PluginExtensionPointRuntime_Builder.res +19 -21
  127. package/src/plugin/runtime/PluginRuntime_Builder.res +167 -188
  128. package/src/plugin/runtime/Util_PluginMessage_Runtime.res +6 -5
  129. package/src/plugin/stack/Plugin_Stack.res +104 -103
  130. package/src/util/Util_AlarmSpec.res +2 -1
  131. package/src/util/Util_AppSync.res +8 -2
  132. package/src/util/Util_AppSync_Caller.res +15 -14
  133. package/src/util/Util_Bundle.res +46 -42
  134. package/src/util/Util_CognitoGroupUser_Runtime.res +32 -34
  135. package/src/util/Util_Cognito_Runtime.res +11 -7
  136. package/src/util/Util_DeadLetterQueue.res +12 -2
  137. package/src/util/Util_DynamoDb.res +5 -3
  138. package/src/util/Util_DynamoDbStream.res +7 -3
  139. package/src/util/Util_DynamoDbStream_Runtime.res +1 -2
  140. package/src/util/Util_DynamoDb_Runtime.res +23 -41
  141. package/src/util/Util_HostUiDomain.res +6 -1
  142. package/src/util/Util_Kinesis_Runtime.res +10 -11
  143. package/src/util/Util_Lambda.res +12 -5
  144. package/src/util/Util_LambdaEnvBudget.res +5 -5
  145. package/src/util/Util_LambdaLogging.res +1 -2
  146. package/src/util/Util_LogGroup_Adopting.res +1 -4
  147. package/src/util/Util_ResourceNaming.res +1 -2
  148. package/src/util/Util_SNS.res +8 -3
  149. package/src/util/Util_SNS_FIFO.res +1 -1
  150. package/src/util/Util_SNS_Runtime.res +11 -10
  151. package/src/util/Util_SQS.res +4 -2
  152. package/src/util/Util_SQS_FIFO.res +5 -2
  153. package/src/util/Util_SQS_Runtime.res +37 -36
  154. package/src/util/Util_ShellConfig.res +3 -2
  155. package/src/util/Util_StaticBundle.res +1 -3
  156. package/src/util/Util_StoreLayout.res +4 -1
  157. package/tests/AWS_TagsTest.res +16 -5
  158. package/tests/AppSyncEventsSigner_OpsTest.res +11 -2
  159. package/tests/AppSync_AdapterTest.res +203 -224
  160. package/tests/AppSync_Resolver_RetryingTest.res +64 -44
  161. package/tests/AppSync_RetirementNarrowingTest.res +59 -46
  162. package/tests/AppSync_SdlDecorateTest.res +22 -20
  163. package/tests/AppSync_SourceApiAssociation_RetryingTest.res +40 -22
  164. package/tests/Auth_ActiveRolePoolAttachmentTest.res +114 -107
  165. package/tests/Auth_ActiveRoleStoreTest.res +12 -9
  166. package/tests/Auth_ActiveRoleStore_SchemaTest.res +2 -1
  167. package/tests/Auth_ActiveRoleTrigger_OpsTest.res +20 -16
  168. package/tests/Auth_CognitoTest.res +31 -46
  169. package/tests/AutomationSliceEntryPoint_OpsTest.res +69 -67
  170. package/tests/BakeConvergenceTest.res +22 -25
  171. package/tests/Capability_MessagingTest.res +28 -12
  172. package/tests/CommandTopicQueuePolicyTest.res +1 -2
  173. package/tests/CounterEntryPoint_OpsTest.res +3 -7
  174. package/tests/DcbEventLogStorage_DynamoDb_RuntimeTest.res +306 -210
  175. package/tests/EventCollectorConnectLambdaPreviewTest.res +14 -7
  176. package/tests/EventCollectorEntryPoint_OpsTest.res +12 -4
  177. package/tests/EventCollectorPluginDefinitionAssetTest.res +18 -11
  178. package/tests/EventCollectorReadyGateTest.res +5 -4
  179. package/tests/EventLogStorage_DynamoDb_RuntimeTest.res +1 -2
  180. package/tests/EventMapperEntryPoint_OpsTest.res +1 -3
  181. package/tests/ExtensionPointEntryPoint_OpsTest.res +1 -4
  182. package/tests/HandlerFactoryDispatchTest.res +97 -70
  183. package/tests/MCP_LambdaTest.res +16 -27
  184. package/tests/PgChangeFeedRelay_IntegrationTest.res +123 -93
  185. package/tests/PgChangeFeedRelay_RuntimeTest.res +13 -10
  186. package/tests/PgMigrationEntryPoint_IntegrationTest.res +0 -1
  187. package/tests/PgPipeline_IntegrationTest.res +80 -66
  188. package/tests/PgQueryDbUnion_IntegrationTest.res +1 -2
  189. package/tests/PgQueryResolverEntryPoint_OpsTest.res +2 -6
  190. package/tests/PgQueryResolver_LambdaTest.res +41 -24
  191. package/tests/Platform_AdminScan_OpsTest.res +4 -1
  192. package/tests/Platform_ComponentDefinitions_Lambda_OpsTest.res +137 -129
  193. package/tests/ProjectionEntryPoint_DisplayNameTest.res +7 -3
  194. package/tests/ProjectionEntryPoint_UnionStampTest.res +3 -2
  195. package/tests/ProvisionIdentityTest.res +2 -6
  196. package/tests/QueryDbOwnerIndexTableTest.res +6 -4
  197. package/tests/QueryDbResolvers_AppSyncTest.res +23 -16
  198. package/tests/QueryInterceptor_LambdaTest.res +11 -13
  199. package/tests/ReadModelEntryPoint_OpsTest.res +43 -41
  200. package/tests/RuntimeExtensionEntryPoint_OpsTest.res +4 -3
  201. package/tests/SideEffectEntryPoint_OpsTest.res +23 -18
  202. package/tests/StateChangeDescriptorParityTest.res +28 -26
  203. package/tests/StateTopicKeySchemaTest.res +18 -15
  204. package/tests/StateViewSliceEntryPoint_OpsTest.res +55 -44
  205. package/tests/TaskBucketEntryPoint_OpsTest.res +13 -13
  206. package/tests/Upload_ClaimTest.res +3 -3
  207. package/tests/Util_AwsErrorTest.res +4 -7
  208. package/tests/Util_BundleDependencyClosureTest.res +15 -3
  209. package/tests/Util_BundleEsmLoaderTest.res +3 -3
  210. package/tests/Util_BundleResolveTest.res +4 -6
  211. package/tests/Util_BundleRuntimeExtensionTest.res +17 -18
  212. package/tests/Util_DcbMetricsTest.res +6 -6
  213. package/tests/Util_DynamoDbStreamTest.res +4 -5
  214. package/tests/Util_DynamoDbTest.res +3 -2
  215. package/tests/Util_LambdaEnvBudgetTest.res +22 -18
  216. package/tests/Util_LambdaLoggingTest.res +9 -8
  217. package/tests/Util_LogGroup_AdoptingTest.res +10 -5
  218. package/tests/Util_LogRetentionTest.res +6 -2
  219. package/tests/Util_OwnerScopeEnvTest.res +10 -8
  220. package/tests/Util_SQS_RuntimeTest.res +5 -7
  221. package/tests/Util_ShellConfigTest.res +7 -17
  222. package/tests/Util_StaticBundleTest.res +3 -8
  223. package/tests/Util_StoreLayoutTest.res +17 -12
  224. package/tests/integration/AggregateEntryPoint_IntegrationTest.res +1 -4
  225. package/tests/integration/DcbCommandTopicEntryPoint_IntegrationTest.res +15 -15
  226. package/tests/integration/DcbEventLogStorage_DynamoDb_IntegrationTest.res +296 -232
  227. package/tests/integration/DcbIntegrationHarness.res +8 -8
  228. package/tests/integration/EpCompositeSlice.res +16 -8
  229. package/tests/integration/QueryDbUnion_DynamoDb_IntegrationTest.res +8 -10
  230. package/tests/integration/StateViewSliceEntryPoint_IntegrationTest.res +10 -10
  231. package/tests/integration/SvsTestSlice_Projection.res +1 -3
  232. package/tests/util/Util_AlarmSpecTest.res +62 -49
@@ -28,17 +28,21 @@ describe("AppSync_Adapter.injectAwsAuthAll", () => {
28
28
  testSync("adds the Cognito group directive to all mutation fields", () => {
29
29
  let augmented = AppSync_Adapter.injectAwsAuthAll(baseFragment, ~group="Admin")
30
30
  let parts = decodeFragment(augmented)
31
- parts.mutations->Array.forEach(field => {
32
- expect(field)->toContain(`@aws_cognito_user_pools(cognito_groups: ["Admin"])`)
33
- })
31
+ parts.mutations->Array.forEach(
32
+ field => {
33
+ expect(field)->toContain(`@aws_cognito_user_pools(cognito_groups: ["Admin"])`)
34
+ },
35
+ )
34
36
  })
35
37
 
36
38
  testSync("adds the Cognito group directive to all query fields", () => {
37
39
  let augmented = AppSync_Adapter.injectAwsAuthAll(baseFragment, ~group="Admin")
38
40
  let parts = decodeFragment(augmented)
39
- parts.queries->Array.forEach(field => {
40
- expect(field)->toContain(`@aws_cognito_user_pools(cognito_groups: ["Admin"])`)
41
- })
41
+ parts.queries->Array.forEach(
42
+ field => {
43
+ expect(field)->toContain(`@aws_cognito_user_pools(cognito_groups: ["Admin"])`)
44
+ },
45
+ )
42
46
  })
43
47
 
44
48
  testSync("preserves type definitions unchanged", () => {
@@ -51,9 +55,11 @@ describe("AppSync_Adapter.injectAwsAuthAll", () => {
51
55
  testSync("uses specified group name", () => {
52
56
  let augmented = AppSync_Adapter.injectAwsAuthAll(baseFragment, ~group="SuperAdmin")
53
57
  let parts = decodeFragment(augmented)
54
- parts.mutations->Array.forEach(field => {
55
- expect(field)->toContain(`cognito_groups: ["SuperAdmin"]`)
56
- })
58
+ parts.mutations->Array.forEach(
59
+ field => {
60
+ expect(field)->toContain(`cognito_groups: ["SuperAdmin"]`)
61
+ },
62
+ )
57
63
  })
58
64
 
59
65
  // Regression: `injectAwsAuthAll` used to re-encode the fragment without the
@@ -68,9 +74,11 @@ describe("AppSync_Adapter.injectAwsAuthAll", () => {
68
74
  let parts = decodeFragment(augmented)
69
75
  expect(parts.subscriptions->Array.length)->toBe(original.subscriptions->Array.length)
70
76
  expect(parts.subscriptions->Array.length)->toBeGreaterThan(0)
71
- parts.subscriptions->Array.forEach(field => {
72
- expect(field)->toContain(`@aws_cognito_user_pools(cognito_groups: ["Admin"])`)
73
- })
77
+ parts.subscriptions->Array.forEach(
78
+ field => {
79
+ expect(field)->toContain(`@aws_cognito_user_pools(cognito_groups: ["Admin"])`)
80
+ },
81
+ )
74
82
  })
75
83
 
76
84
  testSync("admin Plugin aggregate subscription fields survive the round-trip", () => {
@@ -100,25 +108,20 @@ describe("AppSync_Adapter.injectAwsAuthAll", () => {
100
108
  describe("AppSync_Adapter.injectAwsAuth", () => {
101
109
  testSync("injects auth only on entries with authorization", () => {
102
110
  // Build a fragment with known entries
103
- let mutationEntries: array<ReventlessInfra.Api.mutationSchemaEntry> =
104
- ReventlessCore.Platform_AdminApi.mutationEntries(~cloner=false)
111
+ let mutationEntries: array<
112
+ ReventlessInfra.Api.mutationSchemaEntry,
113
+ > = ReventlessCore.Platform_AdminApi.mutationEntries(~cloner=false)
105
114
  let queryEntries = ReventlessCore.PluginBaseFragment.queryEntries
106
115
 
107
116
  let baseFragment = ReventlessCore.Platform_AdminApi.baseFragment(~cloner=false)
108
- let augmented = AppSync_Adapter.injectAwsAuth(
109
- baseFragment,
110
- ~mutationEntries,
111
- ~queryEntries,
112
- )
117
+ let augmented = AppSync_Adapter.injectAwsAuth(baseFragment, ~mutationEntries, ~queryEntries)
113
118
  let parts = decodeFragment(augmented)
114
119
 
115
120
  // Query entries have authorization → should carry the directive
116
- let queryWithAuth =
117
- queryEntries->Array.some(entry => entry.authorization->Option.isSome)
121
+ let queryWithAuth = queryEntries->Array.some(entry => entry.authorization->Option.isSome)
118
122
  if queryWithAuth {
119
- let hasAuthQuery = parts.queries->Array.some(field =>
120
- field->String.includes("@aws_cognito_user_pools")
121
- )
123
+ let hasAuthQuery =
124
+ parts.queries->Array.some(field => field->String.includes("@aws_cognito_user_pools"))
122
125
  expect(hasAuthQuery)->toBe(true)
123
126
  }
124
127
  })
@@ -177,59 +180,37 @@ describe("AppSync_Adapter.injectAwsAuth — Stage E2 permission lifting", () =>
177
180
  returnTypeName: single,
178
181
  stateSchema: S.unknown,
179
182
  authorization: None,
180
- permission: ?permission,
183
+ ?permission,
181
184
  }
182
185
 
183
186
  testSync("AllowGroups([\"Admin\"]) emits cognito_groups: [\"Admin\"] on mutation", () => {
184
- let fp = Dict.fromArray([(
185
- "catalog_Category_Archive",
186
- Reventless.Authorization.AllowGroups(["Admin"]),
187
- )])
188
- let entry = mutationEntry(
189
- ~fieldNames=["catalog_Category_Archive"],
190
- ~fieldPermissions=fp,
191
- )
187
+ let fp = Dict.fromArray([
188
+ ("catalog_Category_Archive", Reventless.Authorization.AllowGroups(["Admin"])),
189
+ ])
190
+ let entry = mutationEntry(~fieldNames=["catalog_Category_Archive"], ~fieldPermissions=fp)
192
191
  let frag = makeFragment(["catalog_Category_Archive(id: ID!): String"], [])
193
- let aug = AppSync_Adapter.injectAwsAuth(
194
- frag,
195
- ~mutationEntries=[entry],
196
- ~queryEntries=[],
197
- )
192
+ let aug = AppSync_Adapter.injectAwsAuth(frag, ~mutationEntries=[entry], ~queryEntries=[])
198
193
  let parts = decodeFragment(aug)
199
194
  let m = parts.mutations->Array.getUnsafe(0)
200
195
  expect(m)->toContain(`@aws_cognito_user_pools(cognito_groups: ["Admin"])`)
201
196
  })
202
197
 
203
198
  testSync("AllowGroups multi-group emits comma-separated groups", () => {
204
- let fp = Dict.fromArray([(
205
- "p_X",
206
- Reventless.Authorization.AllowGroups(["Admin", "Editor"]),
207
- )])
199
+ let fp = Dict.fromArray([("p_X", Reventless.Authorization.AllowGroups(["Admin", "Editor"]))])
208
200
  let entry = mutationEntry(~fieldNames=["p_X"], ~fieldPermissions=fp)
209
201
  let frag = makeFragment(["p_X(id: ID!): String"], [])
210
- let aug = AppSync_Adapter.injectAwsAuth(
211
- frag,
212
- ~mutationEntries=[entry],
213
- ~queryEntries=[],
214
- )
202
+ let aug = AppSync_Adapter.injectAwsAuth(frag, ~mutationEntries=[entry], ~queryEntries=[])
215
203
  let parts = decodeFragment(aug)
216
- expect(parts.mutations->Array.getUnsafe(0))->toContain(
217
- `@aws_cognito_user_pools(cognito_groups: ["Admin", "Editor"])`,
218
- )
204
+ expect(
205
+ parts.mutations->Array.getUnsafe(0),
206
+ )->toContain(`@aws_cognito_user_pools(cognito_groups: ["Admin", "Editor"])`)
219
207
  })
220
208
 
221
209
  testSync("AllowAuthenticated emits the group-less Cognito directive", () => {
222
- let fp = Dict.fromArray([(
223
- "p_Add",
224
- Reventless.Authorization.AllowAuthenticated,
225
- )])
210
+ let fp = Dict.fromArray([("p_Add", Reventless.Authorization.AllowAuthenticated)])
226
211
  let entry = mutationEntry(~fieldNames=["p_Add"], ~fieldPermissions=fp)
227
212
  let frag = makeFragment(["p_Add(id: ID!): String"], [])
228
- let aug = AppSync_Adapter.injectAwsAuth(
229
- frag,
230
- ~mutationEntries=[entry],
231
- ~queryEntries=[],
232
- )
213
+ let aug = AppSync_Adapter.injectAwsAuth(frag, ~mutationEntries=[entry], ~queryEntries=[])
233
214
  let parts = decodeFragment(aug)
234
215
  // Open, not ungated: the field carries the group-less directive so it stays
235
216
  // reachable once defaultAction flips to DENY, but names no group.
@@ -242,11 +223,7 @@ describe("AppSync_Adapter.injectAwsAuth — Stage E2 permission lifting", () =>
242
223
  let fp = Dict.fromArray([("p_Hide", Reventless.Authorization.DenyAll)])
243
224
  let entry = mutationEntry(~fieldNames=["p_Hide"], ~fieldPermissions=fp)
244
225
  let frag = makeFragment(["p_Hide(id: ID!): String"], [])
245
- let aug = AppSync_Adapter.injectAwsAuth(
246
- frag,
247
- ~mutationEntries=[entry],
248
- ~queryEntries=[],
249
- )
226
+ let aug = AppSync_Adapter.injectAwsAuth(frag, ~mutationEntries=[entry], ~queryEntries=[])
250
227
  let parts = decodeFragment(aug)
251
228
  expect(parts.mutations->Array.getUnsafe(0))->toContain(`"__deny_all__"`)
252
229
  })
@@ -256,19 +233,9 @@ describe("AppSync_Adapter.injectAwsAuth — Stage E2 permission lifting", () =>
256
233
  ("p_Archive", Reventless.Authorization.AllowGroups(["Admin"])),
257
234
  ("p_Add", Reventless.Authorization.AllowAuthenticated),
258
235
  ])
259
- let entry = mutationEntry(
260
- ~fieldNames=["p_Archive", "p_Add"],
261
- ~fieldPermissions=fp,
262
- )
263
- let frag = makeFragment(
264
- ["p_Archive(id: ID!): String", "p_Add(name: String!): String"],
265
- [],
266
- )
267
- let aug = AppSync_Adapter.injectAwsAuth(
268
- frag,
269
- ~mutationEntries=[entry],
270
- ~queryEntries=[],
271
- )
236
+ let entry = mutationEntry(~fieldNames=["p_Archive", "p_Add"], ~fieldPermissions=fp)
237
+ let frag = makeFragment(["p_Archive(id: ID!): String", "p_Add(name: String!): String"], [])
238
+ let aug = AppSync_Adapter.injectAwsAuth(frag, ~mutationEntries=[entry], ~queryEntries=[])
272
239
  let parts = decodeFragment(aug)
273
240
  let archive = parts.mutations->Array.find(f => f->String.includes("p_Archive"))
274
241
  let add = parts.mutations->Array.find(f => f->String.includes("p_Add"))
@@ -295,16 +262,9 @@ describe("AppSync_Adapter.injectAwsAuth — Stage E2 permission lifting", () =>
295
262
  // paren-first split reliably yields the bare field name.
296
263
  let frag = makeFragment(
297
264
  [],
298
- [
299
- "p_Item(id: ID!): Item",
300
- "p_Items(first: Int, after: String): ItemConnection!",
301
- ],
302
- )
303
- let aug = AppSync_Adapter.injectAwsAuth(
304
- frag,
305
- ~mutationEntries=[],
306
- ~queryEntries=[entry],
265
+ ["p_Item(id: ID!): Item", "p_Items(first: Int, after: String): ItemConnection!"],
307
266
  )
267
+ let aug = AppSync_Adapter.injectAwsAuth(frag, ~mutationEntries=[], ~queryEntries=[entry])
308
268
  let parts = decodeFragment(aug)
309
269
  let item = parts.queries->Array.find(f => f->String.includes("p_Item("))
310
270
  let items = parts.queries->Array.find(f => f->String.includes("p_Items("))
@@ -319,10 +279,7 @@ describe("AppSync_Adapter.injectAwsAuth — Stage E2 permission lifting", () =>
319
279
  testSync("Spec-level permission wins over legacy authorization field", () => {
320
280
  // Legacy {tableName, group} says "Admin"; spec-level says "Manager".
321
281
  // Spec-level must win.
322
- let fp = Dict.fromArray([(
323
- "p_X",
324
- Reventless.Authorization.AllowGroups(["Manager"]),
325
- )])
282
+ let fp = Dict.fromArray([("p_X", Reventless.Authorization.AllowGroups(["Manager"]))])
326
283
  let entry: ReventlessInfra.Api.mutationSchemaEntry = {
327
284
  fieldNames: ["p_X"],
328
285
  commandSchema: S.unknown,
@@ -333,44 +290,36 @@ describe("AppSync_Adapter.injectAwsAuth — Stage E2 permission lifting", () =>
333
290
  fieldPermissions: fp,
334
291
  }
335
292
  let frag = makeFragment(["p_X(id: ID!): String"], [])
336
- let aug = AppSync_Adapter.injectAwsAuth(
337
- frag,
338
- ~mutationEntries=[entry],
339
- ~queryEntries=[],
340
- )
293
+ let aug = AppSync_Adapter.injectAwsAuth(frag, ~mutationEntries=[entry], ~queryEntries=[])
341
294
  let parts = decodeFragment(aug)
342
295
  let m = parts.mutations->Array.getUnsafe(0)
343
296
  expect(m)->toContain(`cognito_groups: ["Manager"]`)
344
297
  expect(m)->not_->toContain(`"Admin"`)
345
298
  })
346
299
 
347
- testSync("AllowAuthenticated on a field overrides the legacy authorization (drops the group)", () => {
348
- let fp = Dict.fromArray([(
349
- "p_X",
350
- Reventless.Authorization.AllowAuthenticated,
351
- )])
352
- let entry: ReventlessInfra.Api.mutationSchemaEntry = {
353
- fieldNames: ["p_X"],
354
- commandSchema: S.unknown,
355
- authorization: {
356
- Reventless.ReadModel.tableName: "Tbl",
357
- group: "Admin",
358
- },
359
- fieldPermissions: fp,
360
- }
361
- let frag = makeFragment(["p_X(id: ID!): String"], [])
362
- let aug = AppSync_Adapter.injectAwsAuth(
363
- frag,
364
- ~mutationEntries=[entry],
365
- ~queryEntries=[],
366
- )
367
- let parts = decodeFragment(aug)
368
- // The spec-level AllowAuthenticated wins over the legacy `authorization.group`:
369
- // the field keeps a directive, but no longer names a group.
370
- let m = parts.mutations->Array.getUnsafe(0)
371
- expect(m)->toContain("@aws_cognito_user_pools")
372
- expect(m)->not_->toContain("cognito_groups")
373
- })
300
+ testSync(
301
+ "AllowAuthenticated on a field overrides the legacy authorization (drops the group)",
302
+ () => {
303
+ let fp = Dict.fromArray([("p_X", Reventless.Authorization.AllowAuthenticated)])
304
+ let entry: ReventlessInfra.Api.mutationSchemaEntry = {
305
+ fieldNames: ["p_X"],
306
+ commandSchema: S.unknown,
307
+ authorization: {
308
+ Reventless.ReadModel.tableName: "Tbl",
309
+ group: "Admin",
310
+ },
311
+ fieldPermissions: fp,
312
+ }
313
+ let frag = makeFragment(["p_X(id: ID!): String"], [])
314
+ let aug = AppSync_Adapter.injectAwsAuth(frag, ~mutationEntries=[entry], ~queryEntries=[])
315
+ let parts = decodeFragment(aug)
316
+ // The spec-level AllowAuthenticated wins over the legacy `authorization.group`:
317
+ // the field keeps a directive, but no longer names a group.
318
+ let m = parts.mutations->Array.getUnsafe(0)
319
+ expect(m)->toContain("@aws_cognito_user_pools")
320
+ expect(m)->not_->toContain("cognito_groups")
321
+ },
322
+ )
374
323
  })
375
324
 
376
325
  // ── Dual-auth (Cognito + IAM) for deploy-time system callers ────────────────
@@ -394,7 +343,9 @@ describe("AppSync_Adapter.injectAwsAuth — systemCallable dual-auth", () => {
394
343
  let entry: ReventlessInfra.Api.mutationSchemaEntry = {
395
344
  fieldNames: ["p_Sync"],
396
345
  commandSchema: S.unknown,
397
- fieldPermissions: Dict.fromArray([("p_Sync", Reventless.Authorization.AllowGroups(["Admin"]))]),
346
+ fieldPermissions: Dict.fromArray([
347
+ ("p_Sync", Reventless.Authorization.AllowGroups(["Admin"])),
348
+ ]),
398
349
  systemCallable: true,
399
350
  }
400
351
  let frag = makeFragment(["p_Sync(id: ID!): String"], [])
@@ -406,21 +357,24 @@ describe("AppSync_Adapter.injectAwsAuth — systemCallable dual-auth", () => {
406
357
  expect(m)->not_->toContain("@aws_auth")
407
358
  })
408
359
 
409
- testSync("systemCallable mutation without a group restriction stays open to Cognito + IAM", () => {
410
- let entry: ReventlessInfra.Api.mutationSchemaEntry = {
411
- fieldNames: ["p_Sync"],
412
- commandSchema: S.unknown,
413
- fieldPermissions: Dict.fromArray([("p_Sync", Reventless.Authorization.AllowAuthenticated)]),
414
- systemCallable: true,
415
- }
416
- let frag = makeFragment(["p_Sync(id: ID!): String"], [])
417
- let aug = AppSync_Adapter.injectAwsAuth(frag, ~mutationEntries=[entry], ~queryEntries=[])
418
- let m = decodeFragment(aug).mutations->Array.getUnsafe(0)
419
- // Bare @aws_cognito_user_pools (any authenticated Cognito user) + IAM.
420
- expect(m)->toContain("@aws_cognito_user_pools @aws_iam")
421
- expect(m)->not_->toContain("cognito_groups")
422
- expect(m)->not_->toContain("@aws_auth")
423
- })
360
+ testSync(
361
+ "systemCallable mutation without a group restriction stays open to Cognito + IAM",
362
+ () => {
363
+ let entry: ReventlessInfra.Api.mutationSchemaEntry = {
364
+ fieldNames: ["p_Sync"],
365
+ commandSchema: S.unknown,
366
+ fieldPermissions: Dict.fromArray([("p_Sync", Reventless.Authorization.AllowAuthenticated)]),
367
+ systemCallable: true,
368
+ }
369
+ let frag = makeFragment(["p_Sync(id: ID!): String"], [])
370
+ let aug = AppSync_Adapter.injectAwsAuth(frag, ~mutationEntries=[entry], ~queryEntries=[])
371
+ let m = decodeFragment(aug).mutations->Array.getUnsafe(0)
372
+ // Bare @aws_cognito_user_pools (any authenticated Cognito user) + IAM.
373
+ expect(m)->toContain("@aws_cognito_user_pools @aws_iam")
374
+ expect(m)->not_->toContain("cognito_groups")
375
+ expect(m)->not_->toContain("@aws_auth")
376
+ },
377
+ )
424
378
 
425
379
  testSync("a non-systemCallable sibling stays Cognito-only (no @aws_iam)", () => {
426
380
  let entry: ReventlessInfra.Api.mutationSchemaEntry = {
@@ -437,11 +391,17 @@ describe("AppSync_Adapter.injectAwsAuth — systemCallable dual-auth", () => {
437
391
  let iamEntry: ReventlessInfra.Api.mutationSchemaEntry = {
438
392
  fieldNames: ["p_Sync"],
439
393
  commandSchema: S.unknown,
440
- fieldPermissions: Dict.fromArray([("p_Sync", Reventless.Authorization.AllowGroups(["Admin"]))]),
394
+ fieldPermissions: Dict.fromArray([
395
+ ("p_Sync", Reventless.Authorization.AllowGroups(["Admin"])),
396
+ ]),
441
397
  systemCallable: true,
442
398
  }
443
399
  let frag = makeFragment(["p_Sync(id: ID!): String", "p_Other(id: ID!): String"], [])
444
- let aug = AppSync_Adapter.injectAwsAuth(frag, ~mutationEntries=[entry, iamEntry], ~queryEntries=[])
400
+ let aug = AppSync_Adapter.injectAwsAuth(
401
+ frag,
402
+ ~mutationEntries=[entry, iamEntry],
403
+ ~queryEntries=[],
404
+ )
445
405
  let parts = decodeFragment(aug)
446
406
  switch parts.mutations->Array.find(f => f->String.includes("p_Other")) {
447
407
  | Some(other) =>
@@ -480,35 +440,39 @@ describe("AppSync_Adapter.injectAwsAuth — systemCallable dual-auth", () => {
480
440
  }
481
441
  })
482
442
 
483
- testSync("systemCallable dual-auth extends to derived query fields (Items/ByIds/By<Index>)", () => {
484
- let entry: ReventlessInfra.Api.querySchemaEntry = {
485
- singleFieldName: "p_Item",
486
- listFieldName: "p_Items",
487
- returnTypeName: "p_Item",
488
- stateSchema: S.unknown,
489
- authorization: None,
490
- permission: Reventless.Authorization.AllowGroups(["Admin"]),
491
- systemCallable: true,
492
- }
493
- let frag = makeFragment(
494
- [],
495
- [
496
- "p_ItemItems(id: ID!, first: Int): p_ItemConnection!",
497
- "p_ItemsByIds(ids: [ID!]!): [p_Item]!",
498
- "p_ItemByOwner(id: ID!, first: Int): p_ItemConnection!",
499
- "q_Unrelated(id: ID!): String",
500
- ],
501
- )
502
- let aug = AppSync_Adapter.injectAwsAuth(frag, ~mutationEntries=[], ~queryEntries=[entry])
503
- let parts = decodeFragment(aug)
504
- parts.queries->Array.forEach(field =>
505
- if field->String.includes("q_Unrelated") {
506
- expect(field)->not_->toContain("@aws_iam")
507
- } else {
508
- expect(field)->toContain("@aws_iam")
443
+ testSync(
444
+ "systemCallable dual-auth extends to derived query fields (Items/ByIds/By<Index>)",
445
+ () => {
446
+ let entry: ReventlessInfra.Api.querySchemaEntry = {
447
+ singleFieldName: "p_Item",
448
+ listFieldName: "p_Items",
449
+ returnTypeName: "p_Item",
450
+ stateSchema: S.unknown,
451
+ authorization: None,
452
+ permission: Reventless.Authorization.AllowGroups(["Admin"]),
453
+ systemCallable: true,
509
454
  }
510
- )
511
- })
455
+ let frag = makeFragment(
456
+ [],
457
+ [
458
+ "p_ItemItems(id: ID!, first: Int): p_ItemConnection!",
459
+ "p_ItemsByIds(ids: [ID!]!): [p_Item]!",
460
+ "p_ItemByOwner(id: ID!, first: Int): p_ItemConnection!",
461
+ "q_Unrelated(id: ID!): String",
462
+ ],
463
+ )
464
+ let aug = AppSync_Adapter.injectAwsAuth(frag, ~mutationEntries=[], ~queryEntries=[entry])
465
+ let parts = decodeFragment(aug)
466
+ parts.queries->Array.forEach(
467
+ field =>
468
+ if field->String.includes("q_Unrelated") {
469
+ expect(field)->not_->toContain("@aws_iam")
470
+ } else {
471
+ expect(field)->toContain("@aws_iam")
472
+ },
473
+ )
474
+ },
475
+ )
512
476
  })
513
477
 
514
478
  // ── Type-level dual-auth ─────────────────────────────────────────────────────
@@ -551,15 +515,20 @@ describe("AppSync_Adapter — type-level dual-auth", () => {
551
515
 
552
516
  testSync("types prefixed by a callable entry's returnTypeName carry the multi-auth pair", () => {
553
517
  let frag = makeFragmentWithTypes(types, ["p_Item(id: ID!): p_Item"])
554
- let aug = AppSync_Adapter.injectAwsAuth(frag, ~mutationEntries=[], ~queryEntries=[callableEntry])
518
+ let aug = AppSync_Adapter.injectAwsAuth(
519
+ frag,
520
+ ~mutationEntries=[],
521
+ ~queryEntries=[callableEntry],
522
+ )
555
523
  let parts = decodeFragment(aug)
556
- parts.types->Array.forEach(decl =>
557
- if decl->String.startsWith("type p_Item") {
558
- expect(decl)->toContain("@aws_cognito_user_pools @aws_iam {")
559
- } else {
560
- // The sibling view's type and the (non-authorizable) input stay untouched.
561
- expect(decl)->not_->toContain("@aws_iam")
562
- }
524
+ parts.types->Array.forEach(
525
+ decl =>
526
+ if decl->String.startsWith("type p_Item") {
527
+ expect(decl)->toContain("@aws_cognito_user_pools @aws_iam {")
528
+ } else {
529
+ // The sibling view's type and the (non-authorizable) input stay untouched.
530
+ expect(decl)->not_->toContain("@aws_iam")
531
+ },
563
532
  )
564
533
  })
565
534
 
@@ -573,25 +542,31 @@ describe("AppSync_Adapter — type-level dual-auth", () => {
573
542
  decodeFragment(aug).types->Array.forEach(decl => expect(decl)->not_->toContain("@aws_iam"))
574
543
  })
575
544
 
576
- testSync("stampSharedIamTypes marks PageInfo + CommandResult members on the assembled SDL", () => {
577
- let sdl = [
578
- "interface Node {\n id: ID!\n}",
579
- "type PageInfo {\n hasNextPage: Boolean!\n}",
580
- "union CommandResult = CommandAccepted | CommandRejected | CommandPending",
581
- "type CommandAccepted {\n msgId: ID!\n}",
582
- "type CommandRejected {\n msgId: ID!\n}",
583
- "type CommandPending {\n msgId: ID!\n}",
584
- "type Untouched {\n id: ID!\n}",
585
- ]->Array.join("\n\n")
586
- let stamped = AppSync_Adapter.stampSharedIamTypes(sdl)
587
- expect(stamped)->toContain("type PageInfo @aws_cognito_user_pools @aws_iam {")
588
- expect(stamped)->toContain("type CommandAccepted @aws_cognito_user_pools @aws_iam {")
589
- expect(stamped)->toContain("type CommandRejected @aws_cognito_user_pools @aws_iam {")
590
- expect(stamped)->toContain("type CommandPending @aws_cognito_user_pools @aws_iam {")
591
- expect(stamped)->toContain("type Untouched {")
592
- // The union declaration itself takes no directive.
593
- expect(stamped)->toContain("union CommandResult = CommandAccepted | CommandRejected | CommandPending")
594
- })
545
+ testSync(
546
+ "stampSharedIamTypes marks PageInfo + CommandResult members on the assembled SDL",
547
+ () => {
548
+ let sdl =
549
+ [
550
+ "interface Node {\n id: ID!\n}",
551
+ "type PageInfo {\n hasNextPage: Boolean!\n}",
552
+ "union CommandResult = CommandAccepted | CommandRejected | CommandPending",
553
+ "type CommandAccepted {\n msgId: ID!\n}",
554
+ "type CommandRejected {\n msgId: ID!\n}",
555
+ "type CommandPending {\n msgId: ID!\n}",
556
+ "type Untouched {\n id: ID!\n}",
557
+ ]->Array.join("\n\n")
558
+ let stamped = AppSync_Adapter.stampSharedIamTypes(sdl)
559
+ expect(stamped)->toContain("type PageInfo @aws_cognito_user_pools @aws_iam {")
560
+ expect(stamped)->toContain("type CommandAccepted @aws_cognito_user_pools @aws_iam {")
561
+ expect(stamped)->toContain("type CommandRejected @aws_cognito_user_pools @aws_iam {")
562
+ expect(stamped)->toContain("type CommandPending @aws_cognito_user_pools @aws_iam {")
563
+ expect(stamped)->toContain("type Untouched {")
564
+ // The union declaration itself takes no directive.
565
+ expect(stamped)->toContain(
566
+ "union CommandResult = CommandAccepted | CommandRejected | CommandPending",
567
+ )
568
+ },
569
+ )
595
570
  })
596
571
 
597
572
  describe("AppSync_Adapter.injectAwsAuthAll — ~iamFieldNames", () => {
@@ -603,20 +578,25 @@ describe("AppSync_Adapter.injectAwsAuthAll — ~iamFieldNames", () => {
603
578
  decodeFragment(baseFragment).mutations
604
579
  ->Array.getUnsafe(0)
605
580
  ->ReventlessCore.GraphQL_Stitcher.extractLeadingName
606
- let augmented =
607
- AppSync_Adapter.injectAwsAuthAll(baseFragment, ~group="Admin", ~iamFieldNames=[firstMutationName])
581
+ let augmented = AppSync_Adapter.injectAwsAuthAll(
582
+ baseFragment,
583
+ ~group="Admin",
584
+ ~iamFieldNames=[firstMutationName],
585
+ )
608
586
  let parts = decodeFragment(augmented)
609
- parts.mutations->Array.forEach(field => {
610
- let name = ReventlessCore.GraphQL_Stitcher.extractLeadingName(field)
611
- if name == firstMutationName {
612
- expect(field)->toContain(`@aws_cognito_user_pools(cognito_groups: ["Admin"])`)
613
- expect(field)->toContain("@aws_iam")
614
- expect(field)->not_->toContain("@aws_auth")
615
- } else {
616
- expect(field)->toContain(`@aws_cognito_user_pools(cognito_groups: ["Admin"])`)
617
- expect(field)->not_->toContain("@aws_iam")
618
- }
619
- })
587
+ parts.mutations->Array.forEach(
588
+ field => {
589
+ let name = ReventlessCore.GraphQL_Stitcher.extractLeadingName(field)
590
+ if name == firstMutationName {
591
+ expect(field)->toContain(`@aws_cognito_user_pools(cognito_groups: ["Admin"])`)
592
+ expect(field)->toContain("@aws_iam")
593
+ expect(field)->not_->toContain("@aws_auth")
594
+ } else {
595
+ expect(field)->toContain(`@aws_cognito_user_pools(cognito_groups: ["Admin"])`)
596
+ expect(field)->not_->toContain("@aws_iam")
597
+ }
598
+ },
599
+ )
620
600
  })
621
601
 
622
602
  testSync("default (no ~iamFieldNames) leaves every field Cognito-only", () => {
@@ -670,10 +650,7 @@ describe("Split mode — empty base fragment", () => {
670
650
 
671
651
  testSync("stitching admin base without plugins produces only admin fields", () => {
672
652
  let adminBase = ReventlessCore.Platform_AdminApi.baseFragment(~cloner=false)
673
- let sdl = ReventlessCore.GraphQL_Stitcher.stitch(
674
- ~baseFragment=adminBase,
675
- ~pluginFragments=[],
676
- )
653
+ let sdl = ReventlessCore.GraphQL_Stitcher.stitch(~baseFragment=adminBase, ~pluginFragments=[])
677
654
  expect(sdl)->toContain("Platform_Plugin")
678
655
  })
679
656
  })
@@ -688,8 +665,9 @@ describe("Split mode — empty base fragment", () => {
688
665
 
689
666
  describe("Merged mode — canonical source documents", () => {
690
667
  let assembleCanonicalSourceSdl = (~baseFragment) =>
691
- AppSync_Adapter.stitchStandaloneWithAwsDirectives(~fragment=baseFragment)
692
- ->AppSync_SdlDecorate.stampCanonicalTypes
668
+ AppSync_Adapter.stitchStandaloneWithAwsDirectives(
669
+ ~fragment=baseFragment,
670
+ )->AppSync_SdlDecorate.stampCanonicalTypes
693
671
 
694
672
  // Mirrors Platform.res's domainBaseFragment: no component fields, one
695
673
  // Platform_ping so the Query type is non-empty.
@@ -755,9 +733,7 @@ describe("Merged mode — canonical source documents", () => {
755
733
  mutations: ["MyPlugin_Item_Create(name: String!): CommandResult"],
756
734
  queries: ["MyPlugin_Item(id: ID!): MyPlugin_Item"],
757
735
  subscriptions: ["onMyPlugin_Item_Create(id: ID): CommandResult"],
758
- subscriptionSources: [
759
- {field: "onMyPlugin_Item_Create", mutations: ["MyPlugin_Item_Create"]},
760
- ],
736
+ subscriptionSources: [{field: "onMyPlugin_Item_Create", mutations: ["MyPlugin_Item_Create"]}],
761
737
  })
762
738
 
763
739
  testSync("plugin subgraph document is standalone: relay types included, node omitted", () => {
@@ -768,13 +744,16 @@ describe("Merged mode — canonical source documents", () => {
768
744
  expect(sdl)->not_->toContain("node(id: ID!): Node")
769
745
  })
770
746
 
771
- testSync("plugin subgraph document carries @aws_subscribe + shared-type IAM stamps, no @canonical", () => {
772
- let sdl = AppSync_Adapter.stitchStandaloneWithAwsDirectives(~fragment=pluginFragment)
773
- expect(sdl)->toContain(`@aws_subscribe(mutations: ["MyPlugin_Item_Create"])`)
774
- expect(sdl)->toContain("type CommandAccepted @aws_cognito_user_pools @aws_iam {")
775
- // Plugin subgraphs stay unstamped — the admin source's canonical defs win.
776
- expect(sdl)->not_->toContain("@canonical")
777
- })
747
+ testSync(
748
+ "plugin subgraph document carries @aws_subscribe + shared-type IAM stamps, no @canonical",
749
+ () => {
750
+ let sdl = AppSync_Adapter.stitchStandaloneWithAwsDirectives(~fragment=pluginFragment)
751
+ expect(sdl)->toContain(`@aws_subscribe(mutations: ["MyPlugin_Item_Create"])`)
752
+ expect(sdl)->toContain("type CommandAccepted @aws_cognito_user_pools @aws_iam {")
753
+ // Plugin subgraphs stay unstamped — the admin source's canonical defs win.
754
+ expect(sdl)->not_->toContain("@canonical")
755
+ },
756
+ )
778
757
  })
779
758
 
780
759
  // ── waitForMergeSuccess — association merge-status poll ─────────────────────