@@ -136,6 +136,129 @@ describe('ObjectSchema', () => {
136136
137137 expect ( ( ) => ObjectSchema . parse ( objectWithFields ) ) . not . toThrow ( ) ;
138138 } ) ;
139+
140+ it ( 'should enforce snake_case for field names' , ( ) => {
141+ // Valid snake_case field names
142+ const validFieldNames = [ 'first_name' , 'last_name' , 'email' , 'company_name' , 'annual_revenue' , '_system_id' ] ;
143+
144+ validFieldNames . forEach ( fieldName => {
145+ const obj = {
146+ name : 'test_object' ,
147+ fields : {
148+ [ fieldName ] : {
149+ type : 'text' as const ,
150+ label : 'Test Field' ,
151+ } ,
152+ } ,
153+ } ;
154+ expect ( ( ) => ObjectSchema . parse ( obj ) ) . not . toThrow ( ) ;
155+ } ) ;
156+ } ) ;
157+
158+ it ( 'should reject PascalCase field names' , ( ) => {
159+ const invalidObject = {
160+ name : 'lead' ,
161+ fields : {
162+ FirstName : {
163+ type : 'text' as const ,
164+ label : '名' ,
165+ } ,
166+ } ,
167+ } ;
168+
169+ expect ( ( ) => ObjectSchema . parse ( invalidObject ) ) . toThrow ( ) ;
170+ expect ( ( ) => ObjectSchema . parse ( invalidObject ) ) . toThrow ( / F i e l d n a m e s m u s t b e l o w e r c a s e s n a k e _ c a s e / ) ;
171+ } ) ;
172+
173+ it ( 'should reject camelCase field names' , ( ) => {
174+ const invalidObject = {
175+ name : 'lead' ,
176+ fields : {
177+ firstName : {
178+ type : 'text' as const ,
179+ label : 'First Name' ,
180+ } ,
181+ } ,
182+ } ;
183+
184+ expect ( ( ) => ObjectSchema . parse ( invalidObject ) ) . toThrow ( ) ;
185+ expect ( ( ) => ObjectSchema . parse ( invalidObject ) ) . toThrow ( / F i e l d n a m e s m u s t b e l o w e r c a s e s n a k e _ c a s e / ) ;
186+ } ) ;
187+
188+ it ( 'should reject kebab-case field names' , ( ) => {
189+ const invalidObject = {
190+ name : 'lead' ,
191+ fields : {
192+ 'first-name' : {
193+ type : 'text' as const ,
194+ label : 'First Name' ,
195+ } ,
196+ } ,
197+ } ;
198+
199+ expect ( ( ) => ObjectSchema . parse ( invalidObject ) ) . toThrow ( ) ;
200+ expect ( ( ) => ObjectSchema . parse ( invalidObject ) ) . toThrow ( / F i e l d n a m e s m u s t b e l o w e r c a s e s n a k e _ c a s e / ) ;
201+ } ) ;
202+
203+ it ( 'should reject field names with spaces' , ( ) => {
204+ const invalidObject = {
205+ name : 'lead' ,
206+ fields : {
207+ 'first name' : {
208+ type : 'text' as const ,
209+ label : 'First Name' ,
210+ } ,
211+ } ,
212+ } ;
213+
214+ expect ( ( ) => ObjectSchema . parse ( invalidObject ) ) . toThrow ( ) ;
215+ expect ( ( ) => ObjectSchema . parse ( invalidObject ) ) . toThrow ( / F i e l d n a m e s m u s t b e l o w e r c a s e s n a k e _ c a s e / ) ;
216+ } ) ;
217+
218+ it ( 'should reject field names starting with numbers' , ( ) => {
219+ const invalidObject = {
220+ name : 'lead' ,
221+ fields : {
222+ '123field' : {
223+ type : 'text' as const ,
224+ label : 'Field' ,
225+ } ,
226+ } ,
227+ } ;
228+
229+ expect ( ( ) => ObjectSchema . parse ( invalidObject ) ) . toThrow ( ) ;
230+ expect ( ( ) => ObjectSchema . parse ( invalidObject ) ) . toThrow ( / F i e l d n a m e s m u s t b e l o w e r c a s e s n a k e _ c a s e / ) ;
231+ } ) ;
232+
233+ it ( 'should reject mixed-case field names like in AI-generated objects' , ( ) => {
234+ // This is the exact problem from the issue
235+ const aiGeneratedObject = {
236+ name : 'lead' ,
237+ label : '线索' ,
238+ fields : {
239+ FirstName : {
240+ type : 'text' as const ,
241+ label : '名' ,
242+ maxLength : 40 ,
243+ } ,
244+ LastName : {
245+ type : 'text' as const ,
246+ label : '姓' ,
247+ required : true ,
248+ maxLength : 80 ,
249+ } ,
250+ Company : {
251+ type : 'text' as const ,
252+ label : '公司' ,
253+ required : true ,
254+ maxLength : 255 ,
255+ } ,
256+ } ,
257+ } ;
258+
259+ expect ( ( ) => ObjectSchema . parse ( aiGeneratedObject ) ) . toThrow ( ) ;
260+ expect ( ( ) => ObjectSchema . parse ( aiGeneratedObject ) ) . toThrow ( / F i e l d n a m e s m u s t b e l o w e r c a s e s n a k e _ c a s e / ) ;
261+ } ) ;
139262 } ) ;
140263
141264 describe ( 'Object Metadata' , ( ) => {
0 commit comments