import type { Dictionary , EntityProperty } from '../typings' ; import { ReferenceType } from '../enums' ; export const enum NodeState { NOT_VISITED = 0 , IN_PROGRESS = 1 , VISITED = 2 } export interface Node { hash : string ; state : NodeState ; dependencies : Dictionary < Edge > ; } export interface Edge { from : string ; to : string ; weight : number ; } internal export class CommitOrderCalculator { /** Matrix of nodes, keys are provided hashes and values are the node definition objects. */ private nodes : Dictionary < Node > = { } ; /** Volatile variable holding calculated nodes during sorting process. */ private sortedNodeList : string [ ] = [ ] ; /**
     * Checks for node existence in graph.
     */ hasNode ( hash ) { return hash in this . nodes ; } /**
     * Adds a new node to the graph, assigning its hash.
     */ addNode ( hash ) { this . nodes [ hash ] = { hash , state : NodeState . NOT_VISITED , dependencies : { } } ; } /**
     * Adds a new dependency (edge) to the graph using their hashes.
     */ addDependency ( from , to , weight ) { this . nodes [ from ] . dependencies [ to ] = { from , to , weight } ; } discoverProperty ( prop , entityName ) { const toOneOwner = ( prop . reference === ReferenceType . ONE_TO_ONE && prop . owner ) || prop . reference === ReferenceType . MANY_TO_ONE ; const toManyOwner = prop . reference === ReferenceType . MANY_TO_MANY && prop . owner && ! prop . pivotTable ; if ( ! toOneOwner && ! toManyOwner ) { return ; } const propertyType = prop . targetMeta ?. root . className ; if ( ! propertyType || ! this . hasNode ( propertyType ) ) { return ; } this . addDependency ( propertyType , entityName , prop . nullable ? 0 : 1 ) ; } internal sort ( ) { for ( const vertex of Object . values ( this . nodes ) ) { if ( vertex . state !== NodeState . NOT_VISITED ) { continue ; } this . visit ( vertex ) ; } const sortedList = this . sortedNodeList . reverse ( ) ; this . nodes = { } ; this . sortedNodeList = [ ] ; return sortedList ; } internal private visit ( node ) { node . state = NodeState . IN_PROGRESS ; for ( const edge of Object . values ( node . dependencies ) ) { const target = this . nodes [ edge . to ] ; switch ( target . state ) { case NodeState . VISITED : break ; case NodeState . IN_PROGRESS : this . visitOpenNode ( node , target , edge ) ; break ; case NodeState . NOT_VISITED : this . visit ( target ) ; } } if ( node . state as unknown !== NodeState . VISITED ) { node . state = NodeState . VISITED ; this . sortedNodeList . push ( node . hash ) ; } } /**
     * Visits all target's dependencies if in cycle with given node
     */ private visitOpenNode ( node , target , edge ) { if ( ! target . dependencies [ node . hash ] || target . dependencies [ node . hash ] . weight >= edge . weight ) { return ; } for ( const edge of Object . values ( target . dependencies ) ) { const targetNode = this . nodes [ edge . to ] ; if ( targetNode . state === NodeState . NOT_VISITED ) { this . visit ( targetNode ) ; } } target . state = NodeState . VISITED ; this . sortedNodeList . push ( target . hash ) ; } }