@@ -37,6 +37,7 @@ import type { Document, MongoError } from 'mongodb';
3737import { MockDataGeneratorStep } from '../components/mock-data-generator-modal/types' ;
3838import type {
3939 FakerSchemaMapping ,
40+ FakerSchema ,
4041 MockDataGeneratorState ,
4142} from '../components/mock-data-generator-modal/types' ;
4243
@@ -181,7 +182,7 @@ export interface FakerMappingGenerationStartedAction {
181182
182183export interface FakerMappingGenerationCompletedAction {
183184 type : CollectionActions . FakerMappingGenerationCompleted ;
184- fakerSchema : FakerSchemaMapping [ ] ;
185+ fakerSchema : FakerSchema ;
185186 requestId : string ;
186187}
187188
@@ -695,64 +696,107 @@ export const cancelSchemaAnalysis = (): CollectionThunkAction<void> => {
695696 } ;
696697} ;
697698
699+ /**
700+ * Transforms LLM array format to keyed object structure.
701+ * Moves fieldPath from object property to object key.
702+ */
703+ function transformFakerSchemaToObject (
704+ fakerSchema : FakerSchemaMapping [ ]
705+ ) : FakerSchema {
706+ const result : FakerSchema = { } ;
707+
708+ for ( const field of fakerSchema ) {
709+ const { fieldPath, ...fieldMapping } = field ;
710+ result [ fieldPath ] = fieldMapping ;
711+ }
712+
713+ return result ;
714+ }
715+
716+ /**
717+ * Checks if the method exists and is callable on the faker object.
718+ */
719+ function isValidFakerMethod ( fakerMethod : string ) : boolean {
720+ const parts = fakerMethod . split ( '.' ) ;
721+
722+ // Validate format: exactly module.method
723+ if ( parts . length !== 2 ) {
724+ return false ;
725+ }
726+
727+ const [ moduleName , methodName ] = parts ;
728+
729+ try {
730+ const fakerModule = ( faker as unknown as Record < string , unknown > ) [
731+ moduleName
732+ ] ;
733+ return (
734+ fakerModule !== null &&
735+ fakerModule !== undefined &&
736+ typeof fakerModule === 'object' &&
737+ typeof ( fakerModule as Record < string , unknown > ) [ methodName ] === 'function'
738+ ) ;
739+ } catch {
740+ return false ;
741+ }
742+ }
743+
698744/**
699745 * Validates a given faker schema against an input schema.
700746 *
701- * - Filters out fields from the faker schema that do not exist in the input schema.
702- * - Validates the `fakerMethod` for each field, marking it as unrecognized if invalid.
703- * - Adds any unmapped input schema fields to the result with an unrecognized faker method.
747+ * - Transforms LLM array format to keyed object structure
748+ * - Validates the `fakerMethod` for each field, marking it as unrecognized if invalid
749+ * - Adds any unmapped input schema fields to the result with an unrecognized faker method
704750 *
705751 * @param inputSchema - The schema definition for the input, mapping field names to their metadata.
706- * @param fakerSchema - The array of faker schema mappings to validate and map.
752+ * @param fakerSchemaArray - The array of faker schema mappings from LLM to validate and map.
707753 * @param logger - Logger instance used to log warnings for invalid faker methods.
708- * @returns An array of validated faker schema mappings, including all input schema fields .
754+ * @returns A keyed object of validated faker schema mappings, with one-to-one fields with input schema.
709755 */
710756const validateFakerSchema = (
711757 inputSchema : Record < string , FieldInfo > ,
712- fakerSchema : FakerSchemaMapping [ ] ,
758+ fakerSchemaArray : FakerSchemaMapping [ ] ,
713759 logger : Logger
714- ) : FakerSchemaMapping [ ] => {
715- const inputSchemaFields = Object . keys ( inputSchema ) ;
716- const validatedFakerSchema = fakerSchema
717- // Drop fields that don't match the input schema structure
718- . filter ( ( field ) => inputSchema [ field . fieldPath ] )
719- . map ( ( field ) => {
720- const { fakerMethod } = field ;
721-
722- // validate faker method
723- const [ moduleName , methodName , ...rest ] = fakerMethod . split ( '.' ) ;
724- if (
725- rest . length > 0 ||
726- typeof ( faker as any ) [ moduleName ] ?. [ methodName ] !== 'function'
727- ) {
760+ ) : FakerSchema => {
761+ // Transform to keyed object structure
762+ const fakerSchemaRaw = transformFakerSchemaToObject ( fakerSchemaArray ) ;
763+
764+ const result : FakerSchema = { } ;
765+
766+ // Process all input schema fields in a single O(n) pass
767+ for ( const fieldPath of Object . keys ( inputSchema ) ) {
768+ const fakerMapping = fakerSchemaRaw [ fieldPath ] ;
769+
770+ if ( fakerMapping ) {
771+ // Validate the faker method
772+ if ( isValidFakerMethod ( fakerMapping . fakerMethod ) ) {
773+ result [ fieldPath ] = fakerMapping ;
774+ } else {
728775 logger . log . warn (
729776 mongoLogId ( 1_001_000_372 ) ,
730777 'Collection' ,
731778 'Invalid faker method' ,
732- { fakerMethod }
779+ { fakerMethod : fakerMapping . fakerMethod }
733780 ) ;
734- return {
735- ... field ,
781+ result [ fieldPath ] = {
782+ mongoType : fakerMapping . mongoType ,
736783 fakerMethod : UNRECOGNIZED_FAKER_METHOD ,
784+ fakerArgs : fakerMapping . fakerArgs ,
785+ probability : fakerMapping . probability ,
737786 } ;
738787 }
788+ } else {
789+ // Field not mapped by LLM - add default
790+ result [ fieldPath ] = {
791+ mongoType : inputSchema [ fieldPath ] . type ,
792+ fakerMethod : UNRECOGNIZED_FAKER_METHOD ,
793+ fakerArgs : [ ] ,
794+ probability : 1 ,
795+ } ;
796+ }
797+ }
739798
740- return field ;
741- } ) ;
742- const unmappedInputFields = inputSchemaFields . filter (
743- ( field ) =>
744- ! validatedFakerSchema . find ( ( { fieldPath } ) => fieldPath === field )
745- ) ;
746- // Default unmapped input fields to "Unrecognized" faker method
747- const unmappedFields = unmappedInputFields . map ( ( field ) => ( {
748- fieldPath : field ,
749- fakerMethod : UNRECOGNIZED_FAKER_METHOD ,
750- mongoType : inputSchema [ field ] . type ,
751- fakerArgs : [ ] ,
752- probability : 1 ,
753- } ) ) ;
754-
755- return [ ...validatedFakerSchema , ...unmappedFields ] ;
799+ return result ;
756800} ;
757801
758802export const generateFakerMappings = ( ) : CollectionThunkAction <
0 commit comments