@@ -215,20 +215,17 @@ function validateGroupRules(group: ParsedRobotsTxt['groups'][number], errors: st
215215 } )
216216 }
217217 else if ( parts . length >= 2 ) {
218- // Path-specific preference like "/path train-ai=n"
219- const path = parts [ 0 ]
220- const preference = parts . slice ( 1 ) . join ( ' ' )
221-
222- if ( ! path ?. startsWith ( '/' ) ) {
223- errors . push ( `Content-Usage path "${ path } " must start with a \`/\`.` )
224- }
225- if ( ! preference . includes ( '=' ) ) {
226- errors . push ( `Content-Usage preference "${ preference } " must contain an assignment (e.g., "train-ai=n").` )
227- }
228- else {
229- // Validate category and value in path-specific rules
230- const preferences = preference . split ( ',' ) . map ( p => p . trim ( ) )
231- preferences . forEach ( ( pref ) => {
218+ const firstPart = parts [ 0 ]
219+ // Check if first part is a preference (contains =) vs a path (starts with /)
220+ // This handles comma-separated values with spaces like "search=y, train-ai=n"
221+ if ( firstPart ?. includes ( '=' ) ) {
222+ // Global preferences with spaces around commas - validate each preference
223+ const allPreferences = rule . split ( ',' ) . map ( p => p . trim ( ) )
224+ allPreferences . forEach ( ( pref ) => {
225+ if ( ! pref . includes ( '=' ) ) {
226+ errors . push ( `Content-Usage rule "${ pref } " must contain a preference assignment (e.g., "train-ai=n").` )
227+ return
228+ }
232229 const [ category , value ] = pref . split ( '=' ) . map ( s => s . trim ( ) )
233230 if ( ! validCategories . includes ( category || '' ) ) {
234231 errors . push ( `Content-Usage category "${ category } " is invalid. Valid categories: ${ validCategories . join ( ', ' ) } .` )
@@ -238,6 +235,31 @@ function validateGroupRules(group: ParsedRobotsTxt['groups'][number], errors: st
238235 }
239236 } )
240237 }
238+ else {
239+ // Path-specific preference like "/path train-ai=n"
240+ const path = firstPart
241+ const preference = parts . slice ( 1 ) . join ( ' ' )
242+
243+ if ( ! path ?. startsWith ( '/' ) ) {
244+ errors . push ( `Content-Usage path "${ path } " must start with a \`/\`.` )
245+ }
246+ if ( ! preference . includes ( '=' ) ) {
247+ errors . push ( `Content-Usage preference "${ preference } " must contain an assignment (e.g., "train-ai=n").` )
248+ }
249+ else {
250+ // Validate category and value in path-specific rules
251+ const preferences = preference . split ( ',' ) . map ( p => p . trim ( ) )
252+ preferences . forEach ( ( pref ) => {
253+ const [ category , value ] = pref . split ( '=' ) . map ( s => s . trim ( ) )
254+ if ( ! validCategories . includes ( category || '' ) ) {
255+ errors . push ( `Content-Usage category "${ category } " is invalid. Valid categories: ${ validCategories . join ( ', ' ) } .` )
256+ }
257+ if ( ! validValues . includes ( value || '' ) ) {
258+ errors . push ( `Content-Usage value "${ value } " for "${ category } " is invalid. Valid values: y, n.` )
259+ }
260+ } )
261+ }
262+ }
241263 }
242264 } )
243265 }
@@ -276,20 +298,17 @@ function validateGroupRules(group: ParsedRobotsTxt['groups'][number], errors: st
276298 } )
277299 }
278300 else if ( parts . length >= 2 ) {
279- // Path-specific preference like "/path ai-train=no"
280- const path = parts [ 0 ]
281- const preference = parts . slice ( 1 ) . join ( ' ' )
282-
283- if ( ! path ?. startsWith ( '/' ) ) {
284- errors . push ( `Content-Signal path "${ path } " must start with a \`/\`.` )
285- }
286- if ( ! preference . includes ( '=' ) ) {
287- errors . push ( `Content-Signal preference "${ preference } " must contain an assignment (e.g., "ai-train=no").` )
288- }
289- else {
290- // Validate category and value in path-specific rules
291- const preferences = preference . split ( ',' ) . map ( p => p . trim ( ) )
292- preferences . forEach ( ( pref ) => {
301+ const firstPart = parts [ 0 ]
302+ // Check if first part is a preference (contains =) vs a path (starts with /)
303+ // This handles comma-separated values with spaces like "search=yes, ai-train=no"
304+ if ( firstPart ?. includes ( '=' ) ) {
305+ // Global preferences with spaces around commas - validate each preference
306+ const allPreferences = rule . split ( ',' ) . map ( p => p . trim ( ) )
307+ allPreferences . forEach ( ( pref ) => {
308+ if ( ! pref . includes ( '=' ) ) {
309+ errors . push ( `Content-Signal rule "${ pref } " must contain a preference assignment (e.g., "ai-train=no").` )
310+ return
311+ }
293312 const [ category , value ] = pref . split ( '=' ) . map ( s => s . trim ( ) )
294313 if ( ! validCategories . includes ( category || '' ) ) {
295314 errors . push ( `Content-Signal category "${ category } " is invalid. Valid categories: ${ validCategories . join ( ', ' ) } .` )
@@ -299,6 +318,31 @@ function validateGroupRules(group: ParsedRobotsTxt['groups'][number], errors: st
299318 }
300319 } )
301320 }
321+ else {
322+ // Path-specific preference like "/path ai-train=no"
323+ const path = firstPart
324+ const preference = parts . slice ( 1 ) . join ( ' ' )
325+
326+ if ( ! path ?. startsWith ( '/' ) ) {
327+ errors . push ( `Content-Signal path "${ path } " must start with a \`/\`.` )
328+ }
329+ if ( ! preference . includes ( '=' ) ) {
330+ errors . push ( `Content-Signal preference "${ preference } " must contain an assignment (e.g., "ai-train=no").` )
331+ }
332+ else {
333+ // Validate category and value in path-specific rules
334+ const preferences = preference . split ( ',' ) . map ( p => p . trim ( ) )
335+ preferences . forEach ( ( pref ) => {
336+ const [ category , value ] = pref . split ( '=' ) . map ( s => s . trim ( ) )
337+ if ( ! validCategories . includes ( category || '' ) ) {
338+ errors . push ( `Content-Signal category "${ category } " is invalid. Valid categories: ${ validCategories . join ( ', ' ) } .` )
339+ }
340+ if ( ! validValues . includes ( value || '' ) ) {
341+ errors . push ( `Content-Signal value "${ value } " for "${ category } " is invalid. Valid values: yes, no.` )
342+ }
343+ } )
344+ }
345+ }
302346 }
303347 } )
304348 }
0 commit comments