Private/Provider/Invoke-PSGalleryApiSearch.ps1

function Invoke-PSGalleryApiSearch {
    <#
    .SYNOPSIS
    Searches PSGallery directly via the NuGet v2 Search API using the fast searchTerm parameter.
    .DESCRIPTION
    Bypasses Find-PSResource which generates slow substringof() OData filters that cause
    PSGallery 500 errors. Uses the native searchTerm parameter instead, which leverages
    the server's full-text search index for fast, reliable results.
    Returns PSObjects compatible with ConvertTo-ModuleInfo -ProviderType PSResourceGet.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$SearchTerm,
        [int]$TotalNeeded = 99
    )

    $galleryUrl = 'https://www.powershellgallery.com/api/v2'
    $apiPageSize = 100
    $apiSkip = 0
    $allEntries = [System.Collections.Generic.List[PSObject]]::new()
    $escapedTerm = $SearchTerm.Replace("'", "''")

    while ($allEntries.Count -lt $TotalNeeded) {
        $batchTop = [Math]::Min($apiPageSize, $TotalNeeded - $allEntries.Count)
        $uri = "${galleryUrl}/Search()?`$filter=IsLatestVersion&searchTerm='${escapedTerm}'&`$top=${batchTop}&`$skip=${apiSkip}"

        Write-PSMBLog -Message "PSGallery API: searchTerm='$SearchTerm' top=$batchTop skip=$apiSkip" -Level 'DEBUG'

        $resp = Invoke-WebRequest -Uri $uri -UseBasicParsing -ErrorAction Stop
        [xml]$feed = $resp.Content

        $nsm = [System.Xml.XmlNamespaceManager]::new($feed.NameTable)
        $nsm.AddNamespace('a', 'http://www.w3.org/2005/Atom')
        $nsm.AddNamespace('d', 'http://schemas.microsoft.com/ado/2007/08/dataservices')
        $nsm.AddNamespace('m', 'http://schemas.microsoft.com/ado/2007/08/dataservices/metadata')

        $entries = $feed.SelectNodes('//a:entry', $nsm)
        if (-not $entries -or $entries.Count -eq 0) { break }

        foreach ($entry in $entries) {
            $p = $entry.SelectSingleNode('m:properties', $nsm)
            if (-not $p) { continue }

            $tagStr = $p.SelectSingleNode('d:Tags', $nsm)?.InnerText
            $tags = if ($tagStr) { @($tagStr -split '\s+' | Where-Object { $_ }) } else { @() }

            if ('PSScript' -in $tags) { continue }

            $pubStr = $p.SelectSingleNode('d:Published', $nsm)?.InnerText
            $published = if ($pubStr) { try { [datetime]$pubStr } catch { [datetime]::MinValue } } else { [datetime]::MinValue }

            $dlStr = $p.SelectSingleNode('d:DownloadCount', $nsm)?.InnerText
            $downloads = if ($dlStr) { try { [long]$dlStr } catch { 0L } } else { 0L }

            $depStr = $p.SelectSingleNode('d:Dependencies', $nsm)?.InnerText
            $deps = if ($depStr) {
                @($depStr -split '\|' | ForEach-Object { ($_ -split ':')[0] } | Where-Object { $_ })
            } else { @() }

            $preStr = $p.SelectSingleNode('d:IsPrerelease', $nsm)?.InnerText

            $allEntries.Add([PSCustomObject]@{
                Name               = $p.SelectSingleNode('d:Id', $nsm)?.InnerText
                Version            = $p.SelectSingleNode('d:Version', $nsm)?.InnerText
                Author             = $p.SelectSingleNode('d:Authors', $nsm)?.InnerText
                Description        = $p.SelectSingleNode('d:Description', $nsm)?.InnerText
                ProjectUri         = $p.SelectSingleNode('d:ProjectUrl', $nsm)?.InnerText
                LicenseUri         = $p.SelectSingleNode('d:LicenseUrl', $nsm)?.InnerText
                Tags               = $tags
                Repository         = 'PSGallery'
                PublishedDate      = $published
                Dependencies       = $deps
                IsPrerelease       = ($preStr -eq 'true')
                AdditionalMetadata = @{ downloadCount = $downloads }
            })

            if ($allEntries.Count -ge $TotalNeeded) { break }
        }

        $apiSkip += $entries.Count
        if ($entries.Count -lt $batchTop) { break }
    }

    return $allEntries.ToArray()
}