@@ -385,19 +385,42 @@ export function mergeFeeds(
385385}
386386
387387// Helper functions for XML generation
388- function escapeXml ( unsafe : string ) : string {
389- if ( ! unsafe ) return "" ;
388+ // Helper to safely coerce a value to a string.
389+ // Handles cases where rss-parser returns an object (e.g., { $: { isPermaLink: "false" }, _: "actual-guid" })
390+ // when XML elements have attributes.
391+ function safeString ( value : unknown ) : string {
392+ if ( value == null ) return "" ;
393+ if ( typeof value === "string" ) return value ;
394+ if ( typeof value === "number" || typeof value === "boolean" )
395+ return String ( value ) ;
396+
397+ if ( typeof value === "object" ) {
398+ // rss-parser stores the text content in _ and attributes in $
399+ const v = value as Record < string , unknown > ;
400+ if ( typeof v . _ === "string" ) return v . _ ;
401+ if ( v . _ != null ) return String ( v . _ ) ;
402+ // If there's no _ property, the element might be attribute-only (malformed)
403+ return "" ;
404+ }
405+
406+ return String ( value ) ;
407+ }
408+
409+ function escapeXml ( unsafe : unknown ) : string {
410+ const str = safeString ( unsafe ) ;
411+ if ( ! str ) return "" ;
390412
391- return unsafe
413+ return str
392414 . replace ( / & / g, "&" )
393415 . replace ( / < / g, "<" )
394416 . replace ( / > / g, ">" )
395417 . replace ( / " / g, """ )
396418 . replace ( / ' / g, "'" ) ;
397419}
398420
399- function wrapCDATA ( content : string ) : string {
400- return `<![CDATA[${ content } ]]>` ;
421+ function wrapCDATA ( content : unknown ) : string {
422+ const str = safeString ( content ) ;
423+ return `<![CDATA[${ str } ]]>` ;
401424}
402425
403426/**
@@ -458,12 +481,7 @@ export function generateRSS(
458481 // Categories
459482 if ( item . categories && item . categories . length > 0 ) {
460483 item . categories . forEach ( ( category : Category ) => {
461- // Handle categories that may be objects with _ property (from rss-parser when they have attributes)
462- const categoryValue =
463- typeof category === "string"
464- ? category
465- : category . _ || String ( category ) ;
466- itemXml += ` <category>${ escapeXml ( categoryValue ) } </category>\n` ;
484+ itemXml += ` <category>${ escapeXml ( category ) } </category>\n` ;
467485 } ) ;
468486 }
469487
@@ -507,17 +525,15 @@ export function generateJSONFeed(
507525 home_page_url : mergedFeed . link ,
508526 feed_url : requestUrl ,
509527 items : mergedFeed . items . map ( ( item ) => ( {
510- id : item . guid || item . link || crypto . randomUUID ( ) ,
528+ id : safeString ( item . guid ) || safeString ( item . link ) || crypto . randomUUID ( ) ,
511529 url : item . link ,
512530 title : item . title ,
513531 content_html : item . content ,
514532 content_text : item . contentSnippet ,
515533 date_published : item . isoDate || item . pubDate ,
516534 author : item . creator ? { name : item . creator } : undefined ,
517535 tags : item . categories
518- ? item . categories . map ( ( cat : any ) =>
519- typeof cat === "string" ? cat : cat . _ || String ( cat ) ,
520- )
536+ ? item . categories . map ( ( cat ) => safeString ( cat ) )
521537 : undefined ,
522538 } ) ) ,
523539 } ;
0 commit comments