Support/Package/Eigenverft.Manifested.Sandbox.Package.Dependencies.ps1

<#
    Eigenverft.Manifested.Sandbox.Package.Dependencies
#>


function Resolve-PackageModelDependencyStack {
    [CmdletBinding()]
    param(
        [AllowNull()]
        [object[]]$DependencyStack
    )

    if ($DependencyStack) {
        return @($DependencyStack | ForEach-Object { [string]$_ })
    }

    return @()
}

function Resolve-PackageModelDependencyCommandEntryPoints {
    [CmdletBinding()]
    param(
        [AllowNull()]
        [psobject]$DependencyResult
    )

    if (-not $DependencyResult -or -not $DependencyResult.PSObject.Properties['EntryPoints'] -or -not $DependencyResult.EntryPoints) {
        if ($DependencyResult -and $DependencyResult.PSObject.Properties['Commands']) {
            return @($DependencyResult.Commands)
        }
        return @()
    }
    if (-not $DependencyResult.EntryPoints.PSObject.Properties['Commands']) {
        return @()
    }

    return @($DependencyResult.EntryPoints.Commands)
}

function Resolve-PackageModelDependencies {
<#
.SYNOPSIS
Ensures direct PackageModel dependencies for the selected definition.
 
.DESCRIPTION
Runs definition-level dependencies before acquisition/install. This is a
minimal direct dependency pass, not a general dependency graph solver.
 
.PARAMETER PackageModelResult
The current PackageModel result object.
 
.EXAMPLE
Resolve-PackageModelDependencies -PackageModelResult $result
#>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [psobject]$PackageModelResult,

        [object[]]$DependencyStack = @()
    )

    $definition = $PackageModelResult.PackageModelConfig.Definition
    $dependencyRecords = New-Object System.Collections.Generic.List[object]
    $seenDependencyIds = New-Object 'System.Collections.Generic.HashSet[string]' ([System.StringComparer]::OrdinalIgnoreCase)
    if (-not $definition.PSObject.Properties['dependencies'] -or $null -eq $definition.dependencies) {
        $PackageModelResult.Dependencies = @()
        return $PackageModelResult
    }

    $currentStack = @(Resolve-PackageModelDependencyStack -DependencyStack $DependencyStack)
    if (-not $currentStack) {
        $currentStack = @([string]$PackageModelResult.DefinitionId)
    }

    foreach ($dependency in @($definition.dependencies)) {
        if (-not $dependency.PSObject.Properties['definitionId'] -or [string]::IsNullOrWhiteSpace([string]$dependency.definitionId)) {
            throw "PackageModel definition '$($definition.id)' has dependency without definitionId."
        }

        $dependencyDefinitionId = [string]$dependency.definitionId
        if ([string]::Equals($dependencyDefinitionId, [string]$PackageModelResult.DefinitionId, [System.StringComparison]::OrdinalIgnoreCase)) {
            throw "PackageModel definition '$($PackageModelResult.DefinitionId)' cannot depend on itself."
        }
        if (-not $seenDependencyIds.Add($dependencyDefinitionId)) {
            continue
        }
        if ($currentStack -contains $dependencyDefinitionId) {
            throw ("PackageModel dependency cycle detected: {0} -> {1}." -f (($currentStack -join ' -> ')), $dependencyDefinitionId)
        }

        Write-PackageModelExecutionMessage -Message ("[STEP] Ensuring package dependency '{0}'." -f $dependencyDefinitionId)
        $dependencyResult = Invoke-PackageModelDefinitionCommand -DefinitionId $dependencyDefinitionId -CommandName ("Invoke-{0}" -f $dependencyDefinitionId) -DependencyStack (@($currentStack) + $dependencyDefinitionId)
        $dependencyStatus = if ($dependencyResult) { [string]$dependencyResult.Status } else { '<none>' }
        if (-not $dependencyResult -or -not [string]::Equals($dependencyStatus, 'Ready', [System.StringComparison]::OrdinalIgnoreCase)) {
            throw "PackageModel dependency '$dependencyDefinitionId' did not become ready. Status='$dependencyStatus'."
        }

        $dependencyRecords.Add([pscustomobject]@{
            DefinitionId   = $dependencyDefinitionId
            Status         = $dependencyStatus
            InstallOrigin  = [string]$dependencyResult.InstallOrigin
            InstallStatus  = if ($dependencyResult.Install -and $dependencyResult.Install.PSObject.Properties['Status']) { [string]$dependencyResult.Install.Status } else { $null }
            EntryPoints    = if ($dependencyResult.PSObject.Properties['EntryPoints']) { $dependencyResult.EntryPoints } else { $null }
            Commands       = @(Resolve-PackageModelDependencyCommandEntryPoints -DependencyResult $dependencyResult)
            Result         = $dependencyResult
        }) | Out-Null

        Write-PackageModelExecutionMessage -Message ("[STATE] Package dependency ready: definition='{0}', installOrigin='{1}', installStatus='{2}'." -f $dependencyDefinitionId, [string]$dependencyResult.InstallOrigin, $(if ($dependencyResult.Install -and $dependencyResult.Install.PSObject.Properties['Status']) { [string]$dependencyResult.Install.Status } else { '<none>' }))
    }

    $PackageModelResult.Dependencies = @($dependencyRecords.ToArray())
    return $PackageModelResult
}

function Resolve-PackageModelDependencyCommandPath {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [psobject]$PackageModelResult,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$CommandName
    )

    foreach ($dependency in @($PackageModelResult.Dependencies)) {
        foreach ($command in @(Resolve-PackageModelDependencyCommandEntryPoints -DependencyResult $dependency)) {
            if ([string]::Equals([string]$command.Name, $CommandName, [System.StringComparison]::OrdinalIgnoreCase) -and
                -not [string]::IsNullOrWhiteSpace([string]$command.Path) -and
                (Test-Path -LiteralPath ([string]$command.Path) -PathType Leaf)) {
                return [pscustomobject]@{
                    DefinitionId = [string]$dependency.DefinitionId
                    Command      = $CommandName
                    CommandPath  = [System.IO.Path]::GetFullPath([string]$command.Path)
                }
            }
        }
    }

    throw "PackageModel install for '$($PackageModelResult.PackageId)' requires installer command '$CommandName', but no ready dependency exposes that command."
}