diff --git a/bundles/af-core/pom.xml b/bundles/af-core/pom.xml
index 005a72f7b5..724b01a800 100644
--- a/bundles/af-core/pom.xml
+++ b/bundles/af-core/pom.xml
@@ -472,6 +472,13 @@
apis
+
+ com.adobe.aem
+ sites-dataplane
+ 1.0.113-SNAPSHOT
+ provided
+
+
diff --git a/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/dataplane/FormsComponentDefinitionEnricher.java b/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/dataplane/FormsComponentDefinitionEnricher.java
new file mode 100644
index 0000000000..7e1c7867ab
--- /dev/null
+++ b/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/dataplane/FormsComponentDefinitionEnricher.java
@@ -0,0 +1,263 @@
+package com.adobe.cq.forms.core.components.internal.dataplane;
+
+import com.adobe.aem.wcm.dataplane.openapimodels.pages.PageContentDefinitionComponentDefinitionsInner;
+import com.adobe.aem.wcm.dataplane.openapimodels.pages.PageContentDefinitionComponentDefinitionsInnerFieldsInner;
+import com.adobe.aem.wcm.dataplane.openapimodels.pages.PageContentDefinitionComponentDefinitionsInnerFieldsInnerOptionsInner;
+import com.adobe.aem.wcm.dataplane.schema.spi.ComponentDefinitionEnricher;
+import com.adobe.aem.wcm.dataplane.schema.spi.ComponentDefinitionEnrichmentContext;
+import com.adobe.aemds.guide.model.FormMetaData;
+import com.day.cq.wcm.foundation.forms.FormsManager;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ValueMap;
+import org.osgi.service.component.annotations.Component;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@Component(service = ComponentDefinitionEnricher.class)
+public class FormsComponentDefinitionEnricher implements ComponentDefinitionEnricher {
+
+ private static final String GUIDE_CONTAINER_V1 = "core/fd/components/form/container/v1/container";
+ private static final String GUIDE_CONTAINER_V2 = "core/fd/components/form/container/v2/container";
+ private static final String GUIDE_CONTAINER_EDS = "fd/franklin/components/form/v1/form";
+ private static final String ACTION_TYPE_FIELD = "./actionType";
+ private static final String PREFILL_SERVICE_FIELD = "./prefillService";
+ private static final String DATASOURCE_NODE = "datasource";
+ private static final String GUIDE_DATA_MODEL = "guideDataModel";
+ private static final String NONE_OPTION_LABEL = "None";
+
+ @Override
+ public void enrich(ComponentDefinitionEnrichmentContext context) {
+ FormMetaData formMetaData = context.getResourceResolver().adaptTo(FormMetaData.class);
+ if (formMetaData == null) {
+ return;
+ }
+
+ Map componentDefinitions =
+ context.getComponentDefinitions();
+ componentDefinitions.entrySet().stream()
+ .filter(entry -> isGuideContainerComponent(entry.getValue()))
+ .map(Map.Entry::getKey)
+ .collect(Collectors.toList())
+ .forEach(componentType -> enrichContainer(componentType, componentDefinitions, context, formMetaData));
+ }
+
+ private void enrichContainer(
+ String componentType,
+ Map componentDefinitions,
+ ComponentDefinitionEnrichmentContext context,
+ FormMetaData formMetaData) {
+ PageContentDefinitionComponentDefinitionsInner containerDefinition = componentDefinitions.get(componentType);
+ if (containerDefinition == null || containerDefinition.getFields() == null) {
+ return;
+ }
+
+ setDynamicOptions(
+ containerDefinition,
+ ACTION_TYPE_FIELD,
+ collectSubmitActionOptions(formMetaData, componentType, context, componentDefinitions)
+ );
+ setDynamicOptions(
+ containerDefinition,
+ PREFILL_SERVICE_FIELD,
+ collectPrefillServiceOptions(formMetaData, context, componentDefinitions)
+ );
+ }
+
+ private List collectSubmitActionOptions(
+ FormMetaData formMetaData,
+ String componentType,
+ ComponentDefinitionEnrichmentContext context,
+ Map componentDefinitions) {
+ String guideDataModel = resolveDatasourceProperty(componentType, ACTION_TYPE_FIELD, GUIDE_DATA_MODEL,
+ context.getResourceResolver());
+ Set uniqueResourceTypes = new HashSet<>();
+ List options = new ArrayList<>();
+ Iterator iterator = formMetaData.getSubmitActions();
+
+ while (iterator != null && iterator.hasNext()) {
+ FormsManager.ComponentDescription description = iterator.next();
+ if (description == null || !uniqueResourceTypes.add(description.getResourceType())) {
+ continue;
+ }
+
+ Resource actionResource = findComponentResource(description.getResourceType(), context.getResourceResolver());
+ if (!matchesGuideDataModel(actionResource, guideDataModel)) {
+ continue;
+ }
+
+ options.add(option(resolveOptionLabel(description), description.getResourceType()));
+ addRuntimeDefinition(description.getResourceType(), componentDefinitions, context);
+ }
+ return options;
+ }
+
+ private List collectPrefillServiceOptions(
+ FormMetaData formMetaData,
+ ComponentDefinitionEnrichmentContext context,
+ Map componentDefinitions) {
+ List options = new ArrayList<>();
+ options.add(option(NONE_OPTION_LABEL, ""));
+
+ Iterator iterator = formMetaData.getPrefillActions();
+ while (iterator != null && iterator.hasNext()) {
+ FormsManager.ComponentDescription description = iterator.next();
+ if (description == null) {
+ continue;
+ }
+
+ options.add(option(resolveOptionLabel(description), description.getResourceType()));
+ addRuntimeDefinition(description.getResourceType(), componentDefinitions, context);
+ }
+ return options;
+ }
+
+ private void addRuntimeDefinition(
+ String componentType,
+ Map componentDefinitions,
+ ComponentDefinitionEnrichmentContext context) {
+ if (componentType == null || componentType.isEmpty() || componentDefinitions.containsKey(componentType)) {
+ return;
+ }
+
+ PageContentDefinitionComponentDefinitionsInner schema = context.resolveComponentDialog(componentType);
+ if (schema != null) {
+ componentDefinitions.put(componentType, schema);
+ }
+ }
+
+ private void setDynamicOptions(
+ PageContentDefinitionComponentDefinitionsInner schema,
+ String fieldName,
+ List options) {
+ if (schema.getFields() == null || options == null || options.isEmpty()) {
+ return;
+ }
+
+ schema.getFields().stream()
+ .filter(field -> fieldName.equals(field.getName()))
+ .findFirst()
+ .ifPresent(field -> field.setOptions(options));
+ }
+
+ private boolean isGuideContainerComponent(PageContentDefinitionComponentDefinitionsInner definition) {
+ if (definition == null) {
+ return false;
+ }
+ return isGuideContainerType(definition.getComponentType()) || isGuideContainerType(definition.getComponentSuperType());
+ }
+
+ private boolean isGuideContainerType(String componentType) {
+ if (componentType == null || componentType.isEmpty()) {
+ return false;
+ }
+ String normalizedComponentType = normalizeComponentType(componentType);
+ // TODO: Support EDS form container enrichment for fd/franklin/components/form/v1/form
+ // once AEM_EDGE content definition exposes compatible runtime-backed form properties.
+ return GUIDE_CONTAINER_V1.equals(normalizedComponentType) || GUIDE_CONTAINER_V2.equals(normalizedComponentType);
+ }
+
+ private boolean matchesGuideDataModel(Resource actionResource, String guideDataModel) {
+ if (guideDataModel == null || guideDataModel.isEmpty()) {
+ return true;
+ }
+ if (actionResource == null) {
+ return false;
+ }
+ String supportedModels = actionResource.getValueMap().get(GUIDE_DATA_MODEL, "");
+ return supportedModels.toLowerCase().contains(guideDataModel.toLowerCase());
+ }
+
+ private String resolveDatasourceProperty(
+ String componentType,
+ String fieldName,
+ String propertyName,
+ ResourceResolver resourceResolver) {
+ Resource componentResource = resolveComponentResource(componentType, resourceResolver);
+ if (componentResource == null) {
+ return "";
+ }
+
+ Resource dialogResource = componentResource.getChild("_cq_dialog");
+ if (dialogResource == null) {
+ return "";
+ }
+
+ Resource fieldResource = findFieldResource(dialogResource, fieldName);
+ if (fieldResource == null) {
+ return "";
+ }
+
+ Resource datasource = fieldResource.getChild(DATASOURCE_NODE);
+ if (datasource == null) {
+ return "";
+ }
+
+ return datasource.getValueMap().get(propertyName, "");
+ }
+
+ private Resource resolveComponentResource(String componentType, ResourceResolver resourceResolver) {
+ if (componentType == null || componentType.isEmpty()) {
+ return null;
+ }
+
+ String normalizedComponentType = normalizeComponentType(componentType);
+ Resource mergedComponentResource = resourceResolver.getResource("/mnt/override/" + normalizedComponentType);
+ if (mergedComponentResource != null) {
+ return mergedComponentResource;
+ }
+
+ return findComponentResource(componentType, resourceResolver);
+ }
+
+ private Resource findComponentResource(String componentType, ResourceResolver resourceResolver) {
+ if (componentType == null || componentType.isEmpty()) {
+ return null;
+ }
+ if (componentType.startsWith("/apps/") || componentType.startsWith("/libs/")) {
+ return resourceResolver.getResource(componentType);
+ }
+
+ Resource appResource = resourceResolver.getResource("/apps/" + componentType);
+ if (appResource != null) {
+ return appResource;
+ }
+ return resourceResolver.getResource("/libs/" + componentType);
+ }
+
+ private Resource findFieldResource(Resource resource, String fieldName) {
+ ValueMap properties = resource.getValueMap();
+ if (fieldName.equals(properties.get("name", ""))) {
+ return resource;
+ }
+
+ for (Resource child : resource.getChildren()) {
+ Resource match = findFieldResource(child, fieldName);
+ if (match != null) {
+ return match;
+ }
+ }
+ return null;
+ }
+
+ private String normalizeComponentType(String componentType) {
+ return componentType.startsWith("/") ? componentType.substring(1) : componentType;
+ }
+
+ private String resolveOptionLabel(FormsManager.ComponentDescription description) {
+ String title = description.getTitle();
+ return title == null || title.isEmpty() ? description.getResourceType() : title;
+ }
+
+ private PageContentDefinitionComponentDefinitionsInnerFieldsInnerOptionsInner option(String name, String value) {
+ return new PageContentDefinitionComponentDefinitionsInnerFieldsInnerOptionsInner()
+ .name(name)
+ .value(value);
+ }
+}
diff --git a/bundles/af-core/src/test/java/com/adobe/cq/forms/core/components/internal/dataplane/FormsComponentDefinitionEnricherTest.java b/bundles/af-core/src/test/java/com/adobe/cq/forms/core/components/internal/dataplane/FormsComponentDefinitionEnricherTest.java
new file mode 100644
index 0000000000..904b09c088
--- /dev/null
+++ b/bundles/af-core/src/test/java/com/adobe/cq/forms/core/components/internal/dataplane/FormsComponentDefinitionEnricherTest.java
@@ -0,0 +1,135 @@
+package com.adobe.cq.forms.core.components.internal.dataplane;
+
+import com.adobe.aem.wcm.dataplane.openapimodels.pages.PageContentDefinitionComponentDefinitionsInner;
+import com.adobe.aem.wcm.dataplane.openapimodels.pages.PageContentDefinitionComponentDefinitionsInnerFieldsInner;
+import com.adobe.aem.wcm.dataplane.schema.spi.ComponentDefinitionEnrichmentContext;
+import com.adobe.aemds.guide.model.FormMetaData;
+import com.day.cq.wcm.foundation.forms.FormsManager;
+import io.wcm.testing.mock.aem.junit5.AemContext;
+import io.wcm.testing.mock.aem.junit5.AemContextExtension;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mockito;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@ExtendWith(AemContextExtension.class)
+class FormsComponentDefinitionEnricherTest {
+
+ private final AemContext context = new AemContext();
+
+ @Test
+ void testEnrich_AddsRuntimeOptionsAndDefinitions() {
+ ResourceResolver resourceResolver = context.resourceResolver();
+ context.create().resource("/mnt/override/core/fd/components/form/container/v2/container/_cq_dialog/content/items/action",
+ "jcr:primaryType", "nt:unstructured",
+ "name", "./actionType");
+ context.create().resource("/mnt/override/core/fd/components/form/container/v2/container/_cq_dialog/content/items/action/datasource",
+ "jcr:primaryType", "nt:unstructured",
+ "guideDataModel", "basic");
+ context.create().resource("/mnt/override/core/fd/components/form/container/v2/container/_cq_dialog/content/items/prefill",
+ "jcr:primaryType", "nt:unstructured",
+ "name", "./prefillService");
+ context.create().resource("/mnt/override/core/fd/components/form/container/v2/container/_cq_dialog/content/items/prefill/datasource",
+ "jcr:primaryType", "nt:unstructured");
+
+ context.create().resource("/libs/fd/af/components/guidesubmittype/restendpoint",
+ "jcr:primaryType", "cq:Component",
+ "guideDataModel", "basic");
+
+ FormMetaData formMetaData = Mockito.mock(FormMetaData.class);
+ Mockito.when(formMetaData.getSubmitActions()).thenReturn(iterator(
+ componentDescription("fd/af/components/guidesubmittype/restendpoint", "REST Endpoint")
+ ));
+ Mockito.when(formMetaData.getPrefillActions()).thenReturn(iterator(
+ componentDescription("com/adobe/forms/prefill/demo", "Demo Prefill")
+ ));
+ context.registerAdapter(ResourceResolver.class, FormMetaData.class, formMetaData);
+
+ Map componentDefinitions = new LinkedHashMap<>();
+ componentDefinitions.put("core/fd/components/form/container/v2/container",
+ schema("Container", "core/fd/components/form/container/v2/container", null,
+ field("./actionType"), field("./prefillService")));
+
+ ComponentDefinitionEnrichmentContext enrichmentContext = Mockito.mock(ComponentDefinitionEnrichmentContext.class);
+ Mockito.when(enrichmentContext.getResourceResolver()).thenReturn(resourceResolver);
+ Mockito.when(enrichmentContext.getComponentDefinitions()).thenReturn(componentDefinitions);
+ Mockito.when(enrichmentContext.resolveComponentDialog("fd/af/components/guidesubmittype/restendpoint"))
+ .thenReturn(schema("REST Endpoint", "fd/af/components/guidesubmittype/restendpoint", null, field("./restEndpointPostUrl")));
+ Mockito.when(enrichmentContext.resolveComponentDialog("com/adobe/forms/prefill/demo"))
+ .thenReturn(schema("Demo Prefill", "com/adobe/forms/prefill/demo", null, field("./serviceUrl")));
+
+ new FormsComponentDefinitionEnricher().enrich(enrichmentContext);
+
+ PageContentDefinitionComponentDefinitionsInner containerDefinition =
+ componentDefinitions.get("core/fd/components/form/container/v2/container");
+ assertNotNull(containerDefinition);
+
+ PageContentDefinitionComponentDefinitionsInnerFieldsInner actionTypeField = fieldByName(containerDefinition, "./actionType");
+ assertNotNull(actionTypeField);
+ assertEquals(1, actionTypeField.getOptions().size());
+ assertEquals("fd/af/components/guidesubmittype/restendpoint", actionTypeField.getOptions().get(0).getValue());
+
+ PageContentDefinitionComponentDefinitionsInnerFieldsInner prefillField = fieldByName(containerDefinition, "./prefillService");
+ assertNotNull(prefillField);
+ assertEquals(2, prefillField.getOptions().size());
+ assertEquals("", prefillField.getOptions().get(0).getValue());
+ assertEquals("com/adobe/forms/prefill/demo", prefillField.getOptions().get(1).getValue());
+
+ assertTrue(componentDefinitions.containsKey("fd/af/components/guidesubmittype/restendpoint"));
+ assertTrue(componentDefinitions.containsKey("com/adobe/forms/prefill/demo"));
+ }
+
+ private PageContentDefinitionComponentDefinitionsInner schema(
+ String title,
+ String componentType,
+ String componentSuperType,
+ PageContentDefinitionComponentDefinitionsInnerFieldsInner... fields) {
+ PageContentDefinitionComponentDefinitionsInner schema = new PageContentDefinitionComponentDefinitionsInner();
+ schema.setTitle(title);
+ schema.setComponentType(componentType);
+ schema.setComponentSuperType(componentSuperType);
+ schema.setFields(new ArrayList<>(List.of(fields)));
+ return schema;
+ }
+
+ private PageContentDefinitionComponentDefinitionsInnerFieldsInner field(String name) {
+ PageContentDefinitionComponentDefinitionsInnerFieldsInner field = new PageContentDefinitionComponentDefinitionsInnerFieldsInner();
+ field.setName(name);
+ return field;
+ }
+
+ private FormsManager.ComponentDescription componentDescription(String resourceType, String title) {
+ FormsManager.ComponentDescription description = Mockito.mock(FormsManager.ComponentDescription.class);
+ Mockito.when(description.getResourceType()).thenReturn(resourceType);
+ Mockito.when(description.getTitle()).thenReturn(title);
+ return description;
+ }
+
+ @SafeVarargs
+ private final Iterator iterator(T... values) {
+ List list = new ArrayList<>();
+ for (T value : values) {
+ list.add(value);
+ }
+ return list.iterator();
+ }
+
+ private PageContentDefinitionComponentDefinitionsInnerFieldsInner fieldByName(
+ PageContentDefinitionComponentDefinitionsInner definition,
+ String name) {
+ return definition.getFields().stream()
+ .filter(field -> name.equals(field.getName()))
+ .findFirst()
+ .orElse(null);
+ }
+}