Private/ConvertTo-SemVer.ps1

function ConvertTo-SemVer {
    <#
    .SYNOPSIS
        Parses a version string into a sortable [semver] object, returning $null for invalid input.
    #>

    [CmdletBinding()]
    param([string]$Version)

    if ([string]::IsNullOrWhiteSpace($Version)) { return $null }

    # Strip leading 'v' if present
    $clean = $Version -replace '^v', ''

    # Normalize two-part versions (e.g. "0.4") to three parts ("0.4.0") so
    # [semver]::TryParse accepts them.
    if ($clean -match '^\d+\.\d+$') {
        $clean = "$clean.0"
    }

    $parsed = $null
    if ([semver]::TryParse($clean, [ref]$parsed)) {
        return $parsed
    }
    return $null
}

function Sort-SemVer {
    <#
    .SYNOPSIS
        Sorts an array of version strings in descending semver order, filtering out invalid
        entries and pre-release versions.
    #>

    [CmdletBinding()]
    param([string[]]$Versions)

    return $Versions |
        ForEach-Object {
            $parsed = ConvertTo-SemVer -Version $_
            if ($parsed -and -not $parsed.PreReleaseLabel) {
                [PSCustomObject]@{ Original = $_; Parsed = $parsed }
            }
        } |
        Sort-Object -Property Parsed -Descending |
        ForEach-Object { $_.Original }
}