@@ -438,9 +438,10 @@ module.exports = class JSONAPISerializer {
438438 * @param {object } data JSON API resource data.
439439 * @param {string } [schema='default'] resource's schema name.
440440 * @param {Map<string, object> } included Included resources.
441+ * @param {string[] } lineage resource identifiers already deserialized to prevent circular references.
441442 * @returns {object } deserialized data.
442443 */
443- deserializeResource ( type , data , schema = 'default' , included ) {
444+ deserializeResource ( type , data , schema = 'default' , included , lineage = [ ] ) {
444445 if ( typeof type === 'object' ) {
445446 type = typeof type . type === 'function' ? type . type ( data ) : get ( data , type . type ) ;
446447 }
@@ -487,73 +488,54 @@ module.exports = class JSONAPISerializer {
487488 } ;
488489
489490 if ( relationship . data !== undefined ) {
490- if ( Array . isArray ( relationship . data ) ) {
491- if ( relationshipOptions && relationshipOptions . alternativeKey ) {
492- set (
493- deserializedData ,
494- relationshipOptions . alternativeKey ,
495- relationship . data . map ( ( d ) => deserializeFunction ( d ) )
496- ) ;
497-
498- if ( included ) {
499- set (
500- deserializedData ,
501- relationshipKey ,
502- relationship . data . map ( ( d ) =>
503- this . deserializeIncluded ( d . type , d . id , relationshipOptions , included )
504- )
505- ) ;
506- }
507- } else {
508- set (
509- deserializedData ,
510- relationshipKey ,
511- relationship . data . map ( ( d ) =>
512- included
513- ? this . deserializeIncluded ( d . type , d . id , relationshipOptions , included )
514- : deserializeFunction ( d )
515- )
516- ) ;
517- }
518- } else if ( relationship . data === null ) {
491+ if ( relationship . data === null ) {
519492 // null data
520493 set (
521494 deserializedData ,
522495 ( relationshipOptions && relationshipOptions . alternativeKey ) || relationshipKey ,
523496 null
524497 ) ;
525- } else if ( relationshipOptions && relationshipOptions . alternativeKey ) {
526- set (
527- deserializedData ,
528- relationshipOptions . alternativeKey ,
529- deserializeFunction ( relationship . data )
530- ) ;
498+ } else {
499+ if ( ( relationshipOptions && relationshipOptions . alternativeKey ) || ! included ) {
500+ set (
501+ deserializedData ,
502+ ( relationshipOptions && relationshipOptions . alternativeKey ) || relationshipKey ,
503+ Array . isArray ( relationship . data )
504+ ? relationship . data . map ( ( d ) => deserializeFunction ( d ) )
505+ : deserializeFunction ( relationship . data )
506+ ) ;
507+ }
531508
532509 if ( included ) {
510+ const deserializeIncludedRelationship = ( relationshipData ) => {
511+ const lineageCopy = [ ...lineage ] ;
512+ // Prevent circular relationships
513+ const isCircular = lineageCopy . includes (
514+ `${ relationshipData . type } -${ relationshipData . id } `
515+ ) ;
516+
517+ if ( isCircular ) {
518+ return deserializeFunction ( data ) ;
519+ }
520+
521+ lineageCopy . push ( `${ type } -${ data . id } ` ) ;
522+ return this . deserializeIncluded (
523+ relationshipData . type ,
524+ relationshipData . id ,
525+ relationshipOptions ,
526+ included ,
527+ lineageCopy
528+ ) ;
529+ } ;
530+
533531 set (
534532 deserializedData ,
535533 relationshipKey ,
536- this . deserializeIncluded (
537- relationship . data . type ,
538- relationship . data . id ,
539- relationshipOptions ,
540- included
541- )
534+ Array . isArray ( relationship . data )
535+ ? relationship . data . map ( ( d ) => deserializeIncludedRelationship ( d ) )
536+ : deserializeIncludedRelationship ( relationship . data )
542537 ) ;
543538 }
544- } else {
545- set (
546- deserializedData ,
547- relationshipKey ,
548- included
549- ? this . deserializeIncluded (
550- relationship . data . type ,
551- relationship . data . id ,
552- relationshipOptions ,
553- included
554- )
555- : deserializeFunction ( relationship . data )
556- ) ;
557539 }
558540 }
559541 } ) ;
@@ -578,7 +560,7 @@ module.exports = class JSONAPISerializer {
578560 return deserializedData ;
579561 }
580562
581- deserializeIncluded ( type , id , relationshipOpts , included ) {
563+ deserializeIncluded ( type , id , relationshipOpts , included , lineage ) {
582564 const includedResource = included . find (
583565 ( resource ) => resource . type === type && resource . id === id
584566 ) ;
@@ -587,7 +569,13 @@ module.exports = class JSONAPISerializer {
587569 return id ;
588570 }
589571
590- return this . deserializeResource ( type , includedResource , relationshipOpts . schema , included ) ;
572+ return this . deserializeResource (
573+ type ,
574+ includedResource ,
575+ relationshipOpts . schema ,
576+ included ,
577+ lineage
578+ ) ;
591579 }
592580
593581 /**
0 commit comments