@formique/semantq 1.1.5 → 1.1.6

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.
package/astToFormique.js CHANGED
@@ -404,6 +404,32 @@ gender: { type: 'radio', priority: 10 },
404
404
 
405
405
  }
406
406
 
407
+
408
+
409
+
410
+ // Helper to handle the "Country-State" -> "Country" transformation
411
+ formatLabel(str) {
412
+ if (!str) return "";
413
+ // Capitalize first letter only
414
+ return str.charAt(0).toUpperCase() + str.slice(1);
415
+ }
416
+
417
+ // NEW: Optimized Mapping Loop
418
+ mapFields() {
419
+ const fieldNode = this.ast.find(node => node.type === 'FormFields');
420
+ if (!fieldNode) return [];
421
+
422
+ fieldNode.fields.forEach(field => {
423
+ // Branching Logic: Check for the new fieldType property
424
+ if (field.fieldType === "dynamicSingleSelect") {
425
+ this.buildDynamicSingleSelect(field);
426
+ } else {
427
+ this.buildStandardField(field);
428
+ }
429
+ });
430
+
431
+ return this.formSchema;
432
+ }
407
433
 
408
434
  // formDirective , formProperties, formProperty, formFields,
409
435
  // optionsAttribute, fieldsAttribute
@@ -445,18 +471,20 @@ inferInputType(fieldName) {
445
471
 
446
472
 
447
473
  cleanFieldName(str) {
448
- const [rawName = '', rawType = ''] = str.split(':');
449
-
450
- const input_name = rawName
451
- .trim()
452
- .replace(/[^\w-]/g, ''); // keeps letters, digits, underscores, hyphens
453
-
454
- const input_type = rawType.trim(); // untouched for now
455
-
474
+ // Get everything before colon (removes :type)
475
+ let rawName = str.split(':')[0];
476
+
477
+ // Remove * and ! markers
478
+ let input_name = rawName.replace(/[*!]/g, '');
479
+
480
+ // Get type after colon (if exists)
481
+ let input_type = str.includes(':') ? str.split(':')[1] : '';
482
+
456
483
  return { input_name, input_type };
457
484
  }
458
485
 
459
486
 
487
+
460
488
  cleanToInputType(str) {
461
489
  //console.log("STR", str);
462
490
 
@@ -642,84 +670,85 @@ getOptionValuesByKey(attributes, targetKey) {
642
670
 
643
671
 
644
672
  buildDynamicSingleSelect(node, rawFieldName) {
645
- const cleanString = this.cleanFieldName(rawFieldName);
646
- const fieldName = cleanString.input_name;
647
-
648
- let fieldSchema = [];
649
- fieldSchema.push('dynamicSingleSelect', fieldName, this.toTitleCase(fieldName));
650
-
651
- let validations;
652
- let attributes;
653
- let mainSelectOptions = []; // Index 5
654
-
655
- let inputParams;
656
-
657
- if (node.attributes.length > 0) {
658
- inputParams = this.handleAttributes(node.attributes);
659
- } else {
660
- inputParams = { validations: {}, attributes: {} }
661
- }
662
-
663
- validations = inputParams.validations;
664
- attributes = inputParams.attributes;
673
+ const attrs = node.attributes || [];
674
+
675
+ // 1. Setup Basic Identity
676
+ const { input_name } = this.cleanFieldName(rawFieldName);
677
+ const cleanName = input_name.toLowerCase().replace(/\s+/g, '-');
678
+ const cleanLabel = this.formatLabel(input_name);
679
+
680
+ // 2. Identify Primary Options
681
+ const optionsAttr = attrs.find(a => a.key === 'options');
682
+ const primaryValues = optionsAttr
683
+ ? (Array.isArray(optionsAttr.values)
684
+ ? optionsAttr.values.map(v => v.value)
685
+ : optionsAttr.value.value.split(',').map(v => v.trim()))
686
+ : [];
665
687
 
666
- if (this.isRequired(rawFieldName)) {
667
- validations['required'] = true;
668
- }
688
+ const mainOptions = primaryValues.map(val => ({
689
+ value: val.toLowerCase().replace(/\s+/g, '-'),
690
+ label: this.formatLabel(val)
691
+ }));
692
+
693
+ // 3. Identify Secondary Scenarios (Attributes whose keys are in primaryValues)
694
+ const scenarioBlocks = attrs
695
+ .filter(a => a.key !== 'options' && primaryValues.includes(a.key))
696
+ .map(attr => {
697
+ const rawVals = Array.isArray(attr.values)
698
+ ? attr.values.map(v => v.value)
699
+ : attr.value.value.split(',').map(v => v.trim());
700
+
701
+ return {
702
+ id: attr.key.toLowerCase().replace(/\s+/g, '-'),
703
+ label: attr.key,
704
+ options: rawVals.map(opt => ({
705
+ value: opt.toLowerCase().replace(/\s+/g, '-'),
706
+ label: this.formatLabel(opt)
707
+ }))
708
+ };
709
+ });
669
710
 
670
- // Extract 'options' (main dropdown values)
671
- if (attributes.options) {
672
- mainSelectOptions = attributes.options;
673
- delete attributes.options;
674
- }
711
+ // 4. Determine Labels (Handle "Country-State" split if present)
712
+ let primaryLabel = cleanLabel;
713
+ let secondaryLabel = "Options";
714
+ if (rawFieldName.includes('-')) {
715
+ const parts = rawFieldName.split('-');
716
+ primaryLabel = this.formatLabel(parts[0].replace(/[*!]/g, ''));
717
+ secondaryLabel = this.formatLabel(parts[1].split(':')[0].replace(/[*!]/g, ''));
718
+ }
719
+ const combinedLabel = `${primaryLabel}-${secondaryLabel}`;
675
720
 
676
- fieldSchema.push(validations); // Index 3
677
- fieldSchema.push(attributes); // Index 4
678
-
679
- // Build scenario blocks (Index 6)
680
- const optionValues = this.extractOptionValues(node.attributes);
681
- let scenarioBlocks = [];
682
-
683
- if (optionValues.length > 0) {
684
- optionValues.forEach(option => {
685
- let schema = {};
686
-
687
- // Use the original option string as the ID
688
- schema['id'] = option;
689
- schema['label'] = option;
690
-
691
- // Get sub-options for this category
692
- const keyOptions = this.getOptionValuesByKey(node.attributes, option);
693
- let options = [];
694
-
695
- if (keyOptions.length > 0) {
696
- keyOptions.forEach(subOption => {
697
- options.push({
698
- value: subOption.toLowerCase(),
699
- label: this.toTitleCase(subOption)
700
- });
701
- });
702
- schema['options'] = options;
703
- scenarioBlocks.push(schema);
704
- }
705
- });
706
- }
721
+ // 5. Validations & Attributes (Excluding the ones used for scenarios)
722
+ const { validations, attributes } = this.handleAttributes(
723
+ attrs.filter(a => a.key === 'options' || !primaryValues.includes(a.key)),
724
+ rawFieldName
725
+ );
707
726
 
708
- fieldSchema.push(mainSelectOptions); // Index 5
709
- fieldSchema.push(scenarioBlocks); // Index 6
727
+ if (this.isRequired(rawFieldName)) {
728
+ validations['required'] = true;
729
+ }
710
730
 
711
- this.formSchema.push(fieldSchema);
731
+ // 6. Build Final Formique Schema
732
+ const dynamicSchema = [
733
+ "dynamicSingleSelect",
734
+ cleanName,
735
+ combinedLabel,
736
+ validations,
737
+ attributes,
738
+ mainOptions,
739
+ scenarioBlocks
740
+ ];
741
+
742
+ this.formSchema.push(dynamicSchema);
743
+ console.log(`Built dynamicSingleSelect: ${cleanName}`);
712
744
  }
713
745
 
714
746
 
715
-
716
-
717
-
718
747
  // Builder methods - implement these according to your needs
719
748
  buildDirective(node) {
720
749
  //console.log(`Processing FormDirective: ${node.name.value}`);
721
750
 
722
- this.formParams['id'] = node.name.value;
751
+ this.formParams['id'] = node.name.value;
723
752
  // Handle directive specific logic
724
753
  }
725
754
 
@@ -803,144 +832,80 @@ if (key === 'sendTo') {
803
832
 
804
833
  buildField(node) {
805
834
  const rawFieldName = node.name;
835
+
836
+ // 1. High-Priority check for Dynamic Select
837
+ if (node.fieldType === 'dynamicSingleSelect') {
838
+ this.buildDynamicSingleSelect(node, rawFieldName);
839
+ return;
840
+ }
841
+
842
+ // 2. Resolve Names and Types
806
843
  const cleanString = this.cleanFieldName(rawFieldName);
807
844
  const cleanFieldName = cleanString.input_name;
845
+ const fieldLabel = this.formatLabel(cleanFieldName);
808
846
 
809
847
  let fieldType;
810
-
811
- // 1. Determine the Field Type
812
848
  if (cleanString.input_type) {
813
849
  fieldType = this.cleanToInputType(cleanString.input_type);
814
850
  } else {
815
- let attributeKeys;
816
- if (node.attributes.length > 0) {
817
- attributeKeys = this.extractAttributeKeys(node.attributes);
818
-
819
- // Check for 'manyof' attribute to force 'checkbox' type
820
- if (attributeKeys.includes('manyof')) {
821
- fieldType = 'checkbox';
822
- } else {
823
- fieldType = this.inputTypeResolver(cleanFieldName, attributeKeys);
824
- }
851
+ const attributeKeys = node.attributes?.length > 0 ? this.extractAttributeKeys(node.attributes) : [];
852
+ if (attributeKeys.includes('manyof')) {
853
+ fieldType = 'checkbox';
825
854
  } else {
826
- fieldType = this.inferInputType(cleanFieldName);
855
+ fieldType = this.inputTypeResolver(cleanFieldName, attributeKeys);
827
856
  }
828
857
  }
829
858
 
830
- // Handle Dynamic Selects (assuming external function)
831
- if (fieldType === 'dynamicSingleSelect') {
832
- this.buildDynamicSingleSelect(node, rawFieldName);
833
- return;
834
- }
835
-
836
- // Initialize Schema Structure
837
- const fieldSchema = [];
838
- const fieldLabel = this.toTitleCase(cleanFieldName);
839
-
840
- // Push Base Definition: [type, name, label]
841
- fieldSchema.push(fieldType, cleanFieldName, fieldLabel);
842
-
859
+ // 3. Process Attributes & Validations
843
860
  let validations = {};
844
861
  let attributes = {};
845
862
 
846
- // 2. Process Attributes and Validations
847
- let inputParams;
848
- if (node.attributes.length > 0) {
849
- // Pass rawFieldName to handleAttributes for specific logic (like 'accept')
850
- inputParams = this.handleAttributes(node.attributes, rawFieldName);
851
- } else {
852
- inputParams = { validations: {}, attributes: {} };
863
+ if (node.attributes && node.attributes.length > 0) {
864
+ const params = this.handleAttributes(node.attributes, rawFieldName);
865
+ validations = params.validations;
866
+ attributes = params.attributes;
853
867
  }
854
868
 
855
- validations = inputParams.validations;
856
- attributes = inputParams.attributes;
857
-
858
- // 3. Clean up Attributes and Add Required Validation
859
- // CRITICAL: We only delete the attributes that are used to build the final list
860
- // but should NOT appear in the attributes object. Dependents/dependsOn SHOULD remain.
861
- delete attributes.options;
862
- delete attributes.selected;
863
- delete attributes.default;
864
- // delete attributes.dependents; // REMOVED: This attribute needs to be in the final object
865
- // delete attributes.dependsOn; // REMOVED: This attribute needs to be in the final object
866
- // delete attributes.condition; // REMOVED: This attribute needs to be in the final object
867
- delete attributes.manyof; // If 'manyof' is not needed in the final attributes, delete it.
868
-
869
+ // 4. Add required validation from asterisk marker
869
870
  if (this.isRequired(rawFieldName)) {
870
871
  validations['required'] = true;
871
872
  }
872
873
 
873
- // Push Validation and Attributes: [..., validations, attributes]
874
- fieldSchema.push(validations);
875
- fieldSchema.push(attributes);
876
-
877
- // 4. Handle Option-Based Fields (The list of choices)
878
- if (node.attributes.length > 0 && (fieldType === 'checkbox' || fieldType === 'radio' || fieldType === 'select' || fieldType === 'multipleSelect' || fieldType === 'singleSelect')) {
879
-
880
- // Helper function to correctly retrieve single string OR array of selected values.
881
- const getSelectedValues = (attributes) => {
882
- const isMultiSelect = (fieldType === 'checkbox' || fieldType === 'multipleSelect');
883
-
884
- // 1. Look for OptionsAttributes (list: selected: a, b)
885
- const selectedOptionsAttr = node.attributes.find(attr =>
886
- attr?.type === "OptionsAttribute" && (attr?.key === "selected" || attr?.key === "default")
887
- );
888
-
889
- if (selectedOptionsAttr && selectedOptionsAttr.values && selectedOptionsAttr.values.length > 0) {
890
- const values = selectedOptionsAttr.values.map(v => v.value.toLowerCase());
891
- return isMultiSelect ? values : values[0]; // array or first item
892
- }
893
-
894
- // 2. Look for FieldAttributes (single: selected: a)
895
- const selectedAttr = node.attributes.find(attr =>
896
- attr?.type === "FieldAttribute" && (attr?.key === "selected" || attr?.key === "default")
897
- );
898
-
899
- if (selectedAttr) {
900
- // Use a safe value retrieval/lower-casing
901
- const rawValue = selectedAttr.value?.value || selectedAttr.value;
902
- const value = (typeof rawValue === 'string' ? rawValue.toLowerCase() : rawValue);
903
-
904
- return isMultiSelect ? [value].filter(Boolean) : value; // array or string
905
- }
874
+ // 5. SPECIAL: Add 'multiple' attribute for multipleSelect
875
+ if (fieldType === 'multipleSelect') {
876
+ attributes['multiple'] = true;
877
+ }
906
878
 
907
- return isMultiSelect ? [] : null;
908
- };
879
+ // 6. Build base schema: [type, name, label, validations, attributes]
880
+ const fieldSchema = [fieldType, cleanFieldName, fieldLabel, validations, attributes];
909
881
 
910
- const isMultiSelection = (fieldType === 'checkbox' || fieldType === 'multipleSelect');
911
- const selectedValues = getSelectedValues(node.attributes);
912
- const optionValues = this.extractOptionValues(node.attributes);
882
+ // 7. Handle Options for Radio/Checkbox/Select fields
883
+ const isOptionField = ['checkbox', 'radio', 'select', 'singleSelect', 'multipleSelect'].includes(fieldType);
884
+ if (isOptionField) {
913
885
  let options = [];
914
-
915
- if (optionValues.length > 0) {
916
- optionValues.forEach(option => {
917
- //const optValue = option.toLowerCase();
918
- const optValue = option;
919
- let isSelected = false;
920
-
921
- if (isMultiSelection) {
922
- // Check if value is IN the array of selected values
923
- isSelected = Array.isArray(selectedValues) && selectedValues.includes(optValue);
924
- } else {
925
- // Check if value EQUALS the single selected string
926
- isSelected = (optValue === selectedValues);
927
- }
928
-
929
- if (isSelected) {
930
- options.push({value: optValue, label: this.toTitleCase(option), selected: true});
931
- } else {
932
- options.push({value: optValue, label: this.toTitleCase(option)});
933
- }
934
- });
935
-
936
- // Push Options Array: [..., validations, attributes, options_array]
937
- fieldSchema.push(options);
886
+
887
+ // Get options from attributes (already processed) or extract from AST
888
+ if (attributes.options && Array.isArray(attributes.options)) {
889
+ options = attributes.options;
890
+ } else {
891
+ const optionValues = this.extractOptionValues(node.attributes || []);
892
+ options = optionValues.map(opt => ({
893
+ value: opt.toLowerCase().replace(/\s+/g, '-'),
894
+ label: this.formatLabel(opt)
895
+ }));
896
+ }
897
+
898
+ if (options.length > 0) {
899
+ fieldSchema.push(options);
938
900
  }
901
+
902
+ // Remove options from attributes to avoid duplication
903
+ delete attributes.options;
939
904
  }
940
905
 
941
- // 5. Finalize Schema
906
+ // 8. Push to schema
942
907
  this.formSchema.push(fieldSchema);
943
- console.log("formSchema", JSON.stringify(this.formSchema,null,2));
908
+ console.log(`Built field: ${cleanFieldName} (${fieldType})`);
944
909
  }
945
910
 
946
911
 
@@ -949,145 +914,109 @@ handleAttributes(attributesAST, fieldName) {
949
914
  let validations = {};
950
915
  let attributes = {};
951
916
 
952
- // Helper to extract the final value from a nested AST node (unchanged)
917
+ /**
918
+ * @helper extractValue
919
+ * Safely extracts the raw value from various node types in the AST.
920
+ */
953
921
  const extractValue = (attrValue) => {
954
- let value;
955
- if (attrValue && typeof attrValue === 'object') {
956
- if (attrValue.value !== undefined) {
957
- value = attrValue.value;
958
- } else {
959
- value = attrValue;
960
- }
961
- } else {
962
- value = attrValue;
922
+ if (!attrValue) return "";
923
+
924
+ // 1. If it's a standard Node (StringLiteral, BooleanLiteral, NumberLiteral)
925
+ if (typeof attrValue === 'object' && attrValue.value !== undefined) {
926
+ return attrValue.value;
963
927
  }
964
- if (typeof value === 'string') {
965
- value = value.trim();
966
- if (value.length >= 2 && value.startsWith("'") && value.endsWith("'")) {
967
- value = value.slice(1, -1);
968
- }
928
+
929
+ // 2. If it's an OptionsAttribute node containing a values array
930
+ if (attrValue.type === 'OptionsAttribute' && Array.isArray(attrValue.values)) {
931
+ return attrValue.values.map(v => (typeof v === 'object' ? v.value : v)).join(', ');
969
932
  }
970
- return value;
933
+
934
+ // 3. Fallback for raw strings or already extracted values
935
+ return attrValue;
971
936
  };
937
+
938
+ // --- STEP 1: Identify and Extract Options ---
939
+ // We look for any attribute with the key 'options'
940
+ let allOptions = [];
941
+ const optionsAttrs = attributesAST.filter(attr => attr.key === 'options');
942
+
943
+ for (const attr of optionsAttrs) {
944
+ // Get the value regardless of whether it's a simple attribute or a list
945
+ let rawValue = extractValue(attr.type === 'OptionsAttribute' ? attr : attr.value);
946
+
947
+ if (rawValue && typeof rawValue === 'string') {
948
+ const parts = rawValue.split(',').map(p => p.trim()).filter(p => p);
949
+ allOptions.push(...parts);
950
+ } else if (Array.isArray(rawValue)) {
951
+ allOptions.push(...rawValue);
952
+ }
953
+ }
972
954
 
973
- // ----------------------------------------------------------------------
974
- // 1. Initial Pass: Process all attributes (including single-value dependents/dependsOn)
975
- // ----------------------------------------------------------------------
955
+ if (allOptions.length > 0) {
956
+ // Map to Formique option format: { value, label }
957
+ attributes['options'] = [...new Set(allOptions)].map(opt => ({
958
+ value: opt.toLowerCase().replace(/\s+/g, '-'),
959
+ label: this.formatLabel(opt)
960
+ }));
961
+ }
962
+
963
+ // --- STEP 2: Process All Other Attributes ---
976
964
  attributesAST.forEach(attr => {
977
965
  const key = attr.key;
966
+ if (key === 'options') return; // Already handled above
978
967
 
979
- if (attr.type === 'FieldAttribute') {
980
- let value = extractValue(attr.value);
981
-
982
- // Only skip list-building keys ('selected', 'default', 'options').
983
- if (this.ignoreAttributes && this.ignoreAttributes.includes(key)) return;
984
- if (['selected', 'default', 'options'].includes(key)) return;
985
-
986
- // Categorize key
987
- if (this.inputAttributes && this.inputAttributes.includes(key)) {
988
- attributes[key] = value;
989
- } else if (this.validationAttributes && this.validationAttributes.includes(key)) {
990
- validations[key] = value;
991
- } else {
992
- // For 'dependents', 'dependsOn', 'manyof', and any unrecognized attributes
993
-
994
- // 💡 CRITICAL FIX: Ensure 'dependents' value is always an array
995
- if (key === 'dependents') {
996
- // This handles AST parsing a single value as a FieldAttribute.
997
- attributes[key] = Array.isArray(value) ? value : [value];
998
- } else {
999
- attributes[key] = value;
1000
- }
1001
- }
1002
- }
1003
- // ----------------------------------------------------------------------
1004
- // 2. Process OptionsAttribute (List) Nodes for 'dependents', 'accept', and 'dependsOn'
1005
- // ----------------------------------------------------------------------
1006
- else if (attr.type === 'OptionsAttribute') {
1007
-
1008
- // ⭐ NEW/FIXED HANDLING: Process 'dependents' (List of field names)
1009
- if (key === 'dependents') {
1010
- const dependentFields = attr.values
1011
- .map(option => extractValue(option))
1012
- .filter(Boolean);
1013
-
1014
- if (dependentFields.length > 0) {
1015
- attributes[key] = dependentFields;
1016
- }
1017
- }
968
+ // Extract the value (handles StringLiterals vs OptionsAttributes)
969
+ let value = extractValue(attr.type === 'OptionsAttribute' ? attr : attr.value);
1018
970
 
1019
- // SPECIAL HANDLING: Process 'accept' for file inputs
1020
- if (key === 'accept' && fieldName.includes(':file')) {
1021
- const acceptValues = attr.values
1022
- .map(option => extractValue(option))
1023
- .filter(Boolean);
1024
-
1025
- if (acceptValues.length > 0) {
1026
- attributes[key] = acceptValues.join(',');
1027
- }
1028
- }
1029
-
1030
- // SPECIAL HANDLING: Process 'dependsOn' (Conditional Logic) if it's a list
1031
- if (key === 'dependsOn' && attr.values && attr.values.length >= 2) {
1032
- const dependsOnValue = extractValue(attr.values[0]);
1033
- const dependsOnCondition = extractValue(attr.values[1]);
1034
-
1035
- if (dependsOnValue && dependsOnCondition) {
1036
- attributes['dependsOn'] = dependsOnValue;
1037
- attributes['condition'] = dependsOnCondition.toLowerCase();
1038
- }
971
+ // Skip internal/meta attributes
972
+ if (this.ignoreAttributes?.includes(key)) return;
973
+ if (['selected', 'default'].includes(key)) return;
974
+
975
+ // --- Logic: Conditional Routing ---
976
+ if (key === 'dependsOn') {
977
+ if (typeof value === 'string' && value.includes(',')) {
978
+ const parts = value.split(',').map(p => p.trim());
979
+ attributes['dependsOn'] = parts[0];
980
+ if (parts[1]) attributes['condition'] = parts[1].toLowerCase();
981
+ } else {
982
+ attributes['dependsOn'] = Array.isArray(value) ? value[0] : value;
1039
983
  }
984
+ return;
1040
985
  }
1041
- });
1042
-
1043
986
 
1044
- // ----------------------------------------------------------------------
1045
- // 3. Handle 'options' Extraction (List of choices) - UNCHANGED
1046
- // ----------------------------------------------------------------------
1047
- const optionsAttr = attributesAST.find(attr => attr.type === "OptionsAttribute" && attr.key === "options");
1048
- if (optionsAttr?.values) {
1049
- const options = optionsAttr.values.map(option => ({
1050
- value: extractValue(option),
1051
- label: extractValue(option) // Simple case: value is also the label
1052
- }));
1053
- if (options.length > 0) {
1054
- attributes['options'] = options;
987
+ if (key === 'condition') {
988
+ attributes['condition'] = typeof value === 'string' ? value.toLowerCase() : value;
989
+ return;
1055
990
  }
1056
- }
1057
991
 
1058
- // ----------------------------------------------------------------------
1059
- // 4. Handle 'selected' (Single or Multi-Select) - UNCHANGED
1060
- // ----------------------------------------------------------------------
1061
- const selectedListAttr = attributesAST.find(attr => attr.type === "OptionsAttribute" && attr.key === "selected");
1062
- if (selectedListAttr && selectedListAttr.values) {
1063
- const selectedValues = selectedListAttr.values
1064
- .map(option => extractValue(option))
1065
- .filter(Boolean);
1066
- if (selectedValues.length > 0) {
1067
- attributes['selected'] = selectedValues;
1068
- }
1069
- } else {
1070
- const selectedSingleAttr = attributesAST.find(attr => attr.type === "FieldAttribute" && attr.key === "selected");
1071
- if (selectedSingleAttr) {
1072
- attributes['selected'] = extractValue(selectedSingleAttr.value);
992
+ if (key === 'dependents') {
993
+ const deps = typeof value === 'string' ? value.split(',').map(v => v.trim()) : value;
994
+ attributes['dependents'] = Array.isArray(deps) ? deps : [deps];
995
+ return;
1073
996
  }
1074
- }
1075
997
 
1076
- // ----------------------------------------------------------------------
1077
- // 5. Handle 'default' (Single Value) - UNCHANGED
1078
- // ----------------------------------------------------------------------
1079
- const defaultAttr = attributesAST.find(attr => attr.type === "FieldAttribute" && attr.key === "default");
1080
- if (defaultAttr) {
1081
- attributes['default'] = extractValue(defaultAttr.value);
1082
- }
998
+ // --- Logic: Validation Extraction ---
999
+ if (this.validationAttributes?.includes(key)) {
1000
+ let finalVal = value;
1001
+ // Type Casting for JSON-safe schema
1002
+ if (value === 'true' || value === true) finalVal = true;
1003
+ else if (value === 'false' || value === false) finalVal = false;
1004
+ else if (!isNaN(value) && typeof value === 'string' && value !== '') {
1005
+ finalVal = Number(value);
1006
+ }
1007
+ validations[key] = finalVal;
1008
+ } else {
1009
+ // All other custom attributes (e.g., placeholder, class, etc.)
1010
+ attributes[key] = value;
1011
+ }
1012
+ });
1083
1013
 
1084
- return {
1085
- validations,
1086
- attributes
1087
- };
1014
+ return { validations, attributes };
1088
1015
  }
1089
1016
 
1090
1017
 
1018
+
1019
+
1091
1020
  buildOptionsAttribute(node) {
1092
1021
  //console.log(`Processing OptionsAttribute with ${node.values.length} values`);
1093
1022
  }
@@ -61,11 +61,11 @@ class Formique extends FormBuilder {
61
61
 
62
62
  if (typeof formDefinition === 'string') {
63
63
  const ast = LowCodeParser.parse(formDefinition.trim());
64
- // console.log("AST", JSON.stringify(ast, null,2));
64
+ //console.log("AST", JSON.stringify(ast, null,2));
65
65
 
66
66
  const formObjects = new astToFormique(ast);
67
67
 
68
- //console.log("formSchema", JSON.stringify(formObjects.formSchema,null,2));
68
+ //console.log("CHECK formSchema", JSON.stringify(formObjects.formSchema,null,2));
69
69
  // Assign from formObjects if a string is passed
70
70
  formSchema = formObjects.formSchema;
71
71
  finalSettings = { ...formSettings, ...formObjects.formSettings };
@@ -1919,7 +1919,7 @@ const telInputValidationAttributes = [
1919
1919
  return `\n${match}\n`;
1920
1920
  }).replace(/\n\s*\n/g, '\n'); // Remove extra blank lines
1921
1921
 
1922
- return formattedHtml;
1922
+ this.formMarkUp += formattedHtml;
1923
1923
  }
1924
1924
 
1925
1925
  renderDateField(type, name, label, validate, attributes) {
@@ -3357,7 +3357,7 @@ renderImageField(type, name, label, validate, attributes) {
3357
3357
  return `\n${match}\n`;
3358
3358
  }).replace(/\n\s*\n/g, '\n');
3359
3359
 
3360
- return formattedHtml;
3360
+ this.formMarkUp += formattedHtml;
3361
3361
  }
3362
3362
 
3363
3363