Public/New-ColorScriptCache.ps1

function Add-UniqueCacheBuildName {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][AllowEmptyCollection()][System.Collections.Generic.HashSet[string]]$NameSet,
        [Parameter(Mandatory)][AllowEmptyCollection()][System.Collections.Generic.List[string]]$NameList,
        [Parameter()][AllowNull()][AllowEmptyString()][string]$Value
    )

    if (-not [string]::IsNullOrWhiteSpace($Value) -and $NameSet.Add($Value)) {
        [void]$NameList.Add($Value)
    }
}

function Get-CacheBuildPipelineName {
    [CmdletBinding()]
    [OutputType([string])]
    param([Parameter()][AllowNull()][object]$InputObject)

    if ($InputObject -is [string]) { return $InputObject }
    if ($null -eq $InputObject -or $InputObject -isnot [System.Management.Automation.PSObject]) { return $null }
    if ($InputObject.PSObject.Properties['Name']) { return [string]$InputObject.PSObject.Properties['Name'].Value }
    if ($InputObject.PSObject.Properties['ScriptName']) { return [string]$InputObject.PSObject.Properties['ScriptName'].Value }
    return $null
}

function Add-FlattenedCacheBuildRecord {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][AllowEmptyCollection()][System.Collections.Generic.List[object]]$RecordList,
        [Parameter()][AllowNull()][object]$InputObject
    )

    if ($null -eq $InputObject) { return }
    $isNestedCollection = $InputObject -is [System.Collections.IEnumerable] -and
    $InputObject -isnot [string] -and
    $InputObject -isnot [System.Management.Automation.PSObject] -and
    $InputObject -isnot [System.Collections.IDictionary]
    if ($isNestedCollection) {
        foreach ($nestedRecord in $InputObject) {
            Add-FlattenedCacheBuildRecord -RecordList $RecordList -InputObject $nestedRecord
        }
        return
    }
    [void]$RecordList.Add($InputObject)
}

function Resolve-ExactCacheBuildRecord {
    [CmdletBinding()]
    [OutputType([object[]])]
    param([Parameter(Mandatory)][AllowEmptyCollection()][string[]]$Name)

    if ($Name.Count -eq 0) { return @() }
    $records = [System.Collections.Generic.List[object]]::new()
    foreach ($requestedName in $Name) {
        if ([System.Management.Automation.WildcardPattern]::ContainsWildcardCharacters($requestedName)) { return @() }
        $record = Get-ColorScriptExactNameRecord -Name $requestedName
        if (-not $record) { return @() }
        [void]$records.Add($record)
    }
    return $records.ToArray()
}

function Get-CacheBuildSourceRecord {
    [CmdletBinding()]
    [OutputType([object[]])]
    param(
        [Parameter(Mandatory)][AllowEmptyCollection()][string[]]$RequestedName,
        [Parameter(Mandatory)][bool]$MetadataFilterRequested,
        [Parameter()][AllowEmptyCollection()][string[]]$Category,
        [Parameter()][AllowEmptyCollection()][string[]]$Tag
    )

    $exactRecords = @()
    if ($RequestedName.Count -gt 0 -and -not $MetadataFilterRequested) {
        $exactRecords = @(Resolve-ExactCacheBuildRecord -Name $RequestedName)
    }
    try {
        if ($exactRecords.Count -eq $RequestedName.Count -and $exactRecords.Count -gt 0) { return $exactRecords }
        if ($RequestedName.Count -eq 0 -and -not $MetadataFilterRequested) { return @(Get-ColorScriptCachePolicyRecord) }
        if ($MetadataFilterRequested) { return @(Get-ColorScriptEntry -Category $Category -Tag $Tag) }
        return @(Get-ColorScriptInventory)
    }
    catch {
        Write-Verbose ("Get-ColorScriptEntry failed: {0}" -f $_.Exception.Message)
        return @()
    }
}

function Get-CacheBuildCandidateRecord {
    [CmdletBinding()]
    [OutputType([object[]])]
    param(
        [Parameter(Mandatory)][AllowEmptyCollection()][string[]]$RequestedName,
        [Parameter()][AllowEmptyCollection()][string[]]$Category,
        [Parameter()][AllowEmptyCollection()][string[]]$Tag,
        [Parameter(Mandatory)][bool]$AllRequested
    )

    $metadataFilterRequested = ($Category -and $Category.Count -gt 0) -or ($Tag -and $Tag.Count -gt 0)
    $sourceRecords = @(Get-CacheBuildSourceRecord -RequestedName $RequestedName -MetadataFilterRequested $metadataFilterRequested -Category $Category -Tag $Tag)
    $normalizedRecords = [System.Collections.Generic.List[object]]::new()
    foreach ($record in $sourceRecords) { Add-FlattenedCacheBuildRecord -RecordList $normalizedRecords -InputObject $record }

    $candidateRecords = $normalizedRecords.ToArray()
    if ($RequestedName.Count -gt 0) {
        $selection = Select-RecordsByName -Records $candidateRecords -Name $RequestedName
        $candidateRecords = @($selection.Records)
        foreach ($pattern in $selection.MissingPatterns) {
            if (-not [string]::IsNullOrWhiteSpace($pattern)) { Write-Warning ($script:Messages.ScriptNotFound -f $pattern) }
        }
    }
    if (-not $AllRequested -and $RequestedName.Count -eq 0 -and -not $metadataFilterRequested -and -not $candidateRecords) {
        $candidateRecords = $normalizedRecords.ToArray()
    }
    return @($candidateRecords | Where-Object { $null -ne $_ })
}

function Get-CacheBuildParallelPreference {
    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param(
        [Parameter(Mandatory)][bool]$ParallelRequested,
        [Parameter(Mandatory)][bool]$ThrottleLimitSpecified,
        [Parameter(Mandatory)][ValidateRange(0, 256)][int]$ThrottleLimit
    )

    $effectiveThrottle = if ($ThrottleLimitSpecified) { $ThrottleLimit } else { [System.Math]::Max(1, [Environment]::ProcessorCount) }
    $useParallel = ($ParallelRequested -or $ThrottleLimitSpecified) -and $effectiveThrottle -gt 1
    if ($useParallel -and $PSVersionTable.PSVersion.Major -lt 7) {
        $message = if ($script:Messages -and $script:Messages.ContainsKey('ParallelCacheNotSupported')) {
            $script:Messages.ParallelCacheNotSupported
        }
        else { 'Parallel cache building requires PowerShell 7 or later. Falling back to sequential execution.' }
        Write-Warning $message
        $useParallel = $false
    }
    return [pscustomobject]@{ UseParallel = $useParallel; ThrottleLimit = $effectiveThrottle }
}

function ConvertTo-OrderedCacheBuildRecord {
    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param(
        [Parameter(Mandatory)][int]$Order,
        [Parameter(Mandatory)][string]$Name,
        [Parameter(Mandatory)][string]$ScriptPath,
        [Parameter()][AllowNull()][string]$CacheFile,
        [Parameter(Mandatory)][string]$Status,
        [Parameter(Mandatory)][AllowEmptyString()][string]$Message,
        [Parameter(Mandatory)][bool]$CacheExists,
        [Parameter()][AllowNull()][object]$ExitCode,
        [Parameter()][AllowEmptyString()][string]$StdOut = '',
        [Parameter()][AllowEmptyString()][string]$StdErr = ''
    )

    return [pscustomobject]@{
        Order  = $Order
        Record = [pscustomobject]@{
            Name        = $Name
            ScriptPath  = $ScriptPath
            CacheFile   = $CacheFile
            Status      = $Status
            Message     = $Message
            CacheExists = $CacheExists
            ExitCode    = $ExitCode
            StdOut      = $StdOut
            StdErr      = $StdErr
        }
    }
}

function Get-CacheBuildRecordIdentity {
    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param([Parameter()][AllowNull()][object]$Record)

    if ($null -eq $Record) { return $null }
    $recordObject = if ($Record -is [System.Management.Automation.PSObject]) { $Record } else { [pscustomobject]$Record }
    $scriptName = [string]$recordObject.Name
    $scriptPath = [string]$recordObject.Path
    if ([string]::IsNullOrWhiteSpace($scriptName) -or [string]::IsNullOrWhiteSpace($scriptPath)) { return $null }
    return [pscustomobject]@{ Name = $scriptName; Path = $scriptPath }
}

function Resolve-CacheBuildCandidate {
    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param(
        [Parameter(Mandatory)][pscustomobject]$Identity,
        [Parameter(Mandatory)][int]$Order,
        [Parameter(Mandatory)][bool]$ImplicitPolicySelection,
        [Parameter(Mandatory)][bool]$Force,
        [Parameter(Mandatory)][bool]$IncludeOutput,
        [Parameter(Mandatory)][System.Management.Automation.PSCmdlet]$Cmdlet
    )

    if (-not $ImplicitPolicySelection -and -not (Test-ColorScriptRequiresCache -ScriptPath $Identity.Path)) {
        $cleanup = Remove-ColorScriptCacheEntry -ScriptName $Identity.Name
        $message = if ($script:Messages -and $script:Messages.ContainsKey('StatusSkippedNotRequired')) { $script:Messages.StatusSkippedNotRequired } else { 'Skipped (caching not required)' }
        $record = ConvertTo-OrderedCacheBuildRecord -Order $Order -Name $Identity.Name -ScriptPath $Identity.Path -CacheFile $(if ($cleanup.CacheExists) { $cleanup.CacheFile } else { $null }) -Status 'SkippedNotRequired' -Message $message -CacheExists $cleanup.CacheExists -ExitCode $null
        return [pscustomobject]@{ BuildRequired = $false; Result = $record }
    }
    if (-not $Force) {
        $cacheEntry = Get-CachedOutput -ScriptPath $Identity.Path -MetadataOnly:(-not $IncludeOutput)
        if ($cacheEntry.Available) {
            $record = ConvertTo-OrderedCacheBuildRecord -Order $Order -Name $Identity.Name -ScriptPath $Identity.Path -CacheFile $cacheEntry.CacheFile -Status 'SkippedUpToDate' -Message $script:Messages.StatusSkippedUpToDate -CacheExists $true -ExitCode 0 -StdOut $cacheEntry.Content
            return [pscustomobject]@{ BuildRequired = $false; Result = $record }
        }
    }
    if (-not (Invoke-ShouldProcess -Cmdlet $Cmdlet -Target $Identity.Name -Action 'Build colorscript cache')) {
        $record = ConvertTo-OrderedCacheBuildRecord -Order $Order -Name $Identity.Name -ScriptPath $Identity.Path -Status 'SkippedByUser' -Message $script:Messages.StatusSkippedByUser -CacheExists $false -ExitCode $null
        return [pscustomobject]@{ BuildRequired = $false; Result = $record }
    }
    return [pscustomobject]@{ BuildRequired = $true; Result = $null; WorkItem = [pscustomobject]@{ Order = $Order; Name = $Identity.Name; Path = $Identity.Path } }
}

function Write-CacheBuildItemProgress {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][string]$Verb,
        [Parameter(Mandatory)][int]$Index,
        [Parameter(Mandatory)][int]$Total,
        [Parameter(Mandatory)][string]$Name,
        [Parameter(Mandatory)][int]$ProgressId,
        [Parameter(Mandatory)][bool]$Quiet
    )

    if ($Quiet) { return }
    $percent = [math]::Min(100, [math]::Max(0, ($Index / $Total) * 100))
    Write-Progress -Id $ProgressId -Activity 'Building colorscript cache' -Status ("{0} {1} of {2}: {3}" -f $Verb, $Index, $Total, $Name) -PercentComplete $percent
}

function Invoke-SequentialCacheBuild {
    [CmdletBinding()]
    [OutputType([pscustomobject[]])]
    param(
        [Parameter(Mandatory)][AllowEmptyCollection()][object[]]$CandidateRecord,
        [Parameter(Mandatory)][bool]$ImplicitPolicySelection,
        [Parameter(Mandatory)][bool]$Force,
        [Parameter(Mandatory)][bool]$IncludeOutput,
        [Parameter(Mandatory)][bool]$Quiet,
        [Parameter(Mandatory)][System.Management.Automation.PSCmdlet]$Cmdlet
    )

    $results = [System.Collections.Generic.List[pscustomobject]]::new()
    $index = 0
    $order = 0
    foreach ($candidate in $CandidateRecord) {
        $index++
        $identity = Get-CacheBuildRecordIdentity -Record $candidate
        if (-not $identity) { continue }
        Write-CacheBuildItemProgress -Verb 'Processing' -Index $index -Total $CandidateRecord.Count -Name $identity.Name -ProgressId 2 -Quiet $Quiet
        $order++
        $resolution = Resolve-CacheBuildCandidate -Identity $identity -Order $order -ImplicitPolicySelection $ImplicitPolicySelection -Force $Force -IncludeOutput $IncludeOutput -Cmdlet $Cmdlet
        if (-not $resolution.BuildRequired) {
            [void]$results.Add($resolution.Result)
            continue
        }
        $operation = Invoke-ColorScriptCacheOperation -ScriptName $identity.Name -ScriptPath $identity.Path -Force:$Force
        if ($operation.Warning) { Write-Warning $operation.Warning }
        [void]$results.Add([pscustomobject]@{ Order = $order; Record = $operation.Result })
    }
    if (-not $Quiet) { Write-Progress -Id 2 -Activity 'Building colorscript cache' -Completed -Status 'Completed' }
    return $results.ToArray()
}

function Get-ParallelCacheBuildPlan {
    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param(
        [Parameter(Mandatory)][AllowEmptyCollection()][object[]]$CandidateRecord,
        [Parameter(Mandatory)][bool]$ImplicitPolicySelection,
        [Parameter(Mandatory)][bool]$Force,
        [Parameter(Mandatory)][bool]$IncludeOutput,
        [Parameter(Mandatory)][bool]$Quiet,
        [Parameter(Mandatory)][System.Management.Automation.PSCmdlet]$Cmdlet
    )

    $results = [System.Collections.Generic.List[pscustomobject]]::new()
    $workQueue = [System.Collections.Generic.List[pscustomobject]]::new()
    $index = 0
    $order = 0
    if (-not $Quiet) { Write-Progress -Id 1 -Activity 'Building colorscript cache' -Status ("Preparing 0 of {0}" -f $CandidateRecord.Count) -PercentComplete 0 }
    foreach ($candidate in $CandidateRecord) {
        $index++
        $identity = Get-CacheBuildRecordIdentity -Record $candidate
        if (-not $identity) { continue }
        Write-CacheBuildItemProgress -Verb 'Preparing' -Index $index -Total $CandidateRecord.Count -Name $identity.Name -ProgressId 1 -Quiet $Quiet
        $order++
        $resolution = Resolve-CacheBuildCandidate -Identity $identity -Order $order -ImplicitPolicySelection $ImplicitPolicySelection -Force $Force -IncludeOutput $IncludeOutput -Cmdlet $Cmdlet
        if ($resolution.BuildRequired) { [void]$workQueue.Add($resolution.WorkItem) } else { [void]$results.Add($resolution.Result) }
    }
    if (-not $Quiet) { Write-Progress -Id 1 -Activity 'Building colorscript cache' -Completed -Status 'Preparation complete' }
    return [pscustomobject]@{ Results = $results; WorkQueue = $workQueue }
}

function Initialize-CacheBuildRunspacePool {
    [CmdletBinding()]
    [OutputType([System.Management.Automation.Runspaces.RunspacePool])]
    param(
        [Parameter(Mandatory)][ValidateRange(1, 256)][int]$ThrottleLimit,
        [Parameter(Mandatory)][string]$ModuleManifest
    )

    $initialState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
    $null = $initialState.ImportPSModule(@($ModuleManifest))
    $runspacePool = $null
    try {
        if ($Host -is [System.Management.Automation.Host.PSHost]) {
            $runspacePool = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspacePool(1, $ThrottleLimit, $initialState, $Host)
        }
    }
    catch { $runspacePool = $null }
    if (-not $runspacePool) {
        try { $runspacePool = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspacePool(1, $ThrottleLimit, $initialState) }
        catch {
            $runspacePool = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspacePool($initialState)
            $null = $runspacePool.SetMinRunspaces(1)
            $null = $runspacePool.SetMaxRunspaces($ThrottleLimit)
        }
    }
    $runspacePool.Open()
    return $runspacePool
}

function Get-CacheBuildWorkerScriptBlock {
    [CmdletBinding()]
    [OutputType([scriptblock])]
    param()

    return {
        param($scriptName, $scriptPath, $forceRebuild, $moduleManifest)
        $moduleInfo = Get-Module -Name 'ColorScripts-Enhanced'
        if (-not $moduleInfo) {
            Import-Module -Name $moduleManifest -Force -ErrorAction Stop
            $moduleInfo = Get-Module -Name 'ColorScripts-Enhanced' -ErrorAction Stop
        }
        $moduleInfo.Invoke({ param($name, $path, $force) Invoke-ColorScriptCacheOperation -ScriptName $name -ScriptPath $path -Force:$force }, $scriptName, $scriptPath, $forceRebuild)
    }
}

function Start-CacheBuildWorkerBatch {
    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param(
        [Parameter(Mandatory)][AllowEmptyCollection()][System.Collections.Generic.List[pscustomobject]]$WorkQueue,
        [Parameter(Mandatory)][AllowEmptyCollection()][System.Collections.Generic.List[pscustomobject]]$JobList,
        [Parameter(Mandatory)][int]$NextWorkIndex,
        [Parameter(Mandatory)][ValidateRange(1, 256)][int]$ThrottleLimit,
        [Parameter(Mandatory)][System.Management.Automation.Runspaces.RunspacePool]$RunspacePool,
        [Parameter(Mandatory)][scriptblock]$WorkerScriptBlock,
        [Parameter(Mandatory)][bool]$Force,
        [Parameter(Mandatory)][string]$ModuleManifest
    )

    $failures = [System.Collections.Generic.List[pscustomobject]]::new()
    while ($NextWorkIndex -lt $WorkQueue.Count -and $JobList.Count -lt $ThrottleLimit) {
        $item = $WorkQueue[$NextWorkIndex]
        $NextWorkIndex++
        $powerShell = [System.Management.Automation.PowerShell]::Create()
        try {
            $powerShell.RunspacePool = $RunspacePool
            $null = $powerShell.AddCommand('Microsoft.PowerShell.Core\Invoke-Command')
            $null = $powerShell.AddParameter('ScriptBlock', $WorkerScriptBlock)
            $null = $powerShell.AddParameter('ArgumentList', @($item.Name, $item.Path, $Force, $ModuleManifest))
            $asyncResult = $powerShell.BeginInvoke()
            [void]$JobList.Add([pscustomobject]@{ PowerShell = $powerShell; Async = $asyncResult; Item = $item })
        }
        catch {
            $powerShell.Dispose()
            $message = $_.Exception.Message
            Write-Warning ("Failed to queue cache worker for {0}: {1}" -f $item.Name, $message)
            [void]$failures.Add((ConvertTo-OrderedCacheBuildRecord -Order $item.Order -Name $item.Name -ScriptPath $item.Path -Status 'Failed' -Message $message -CacheExists $false -ExitCode $null -StdErr $message))
        }
    }
    return [pscustomobject]@{ NextWorkIndex = $NextWorkIndex; Failures = $failures.ToArray() }
}

function Receive-CompletedCacheBuildWorker {
    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param(
        [Parameter(Mandatory)][pscustomobject]$Job
    )

    $outputCollection = $Job.PowerShell.EndInvoke($Job.Async)
    $operation = if ($outputCollection -and $outputCollection.Count -gt 0) { $outputCollection[0] } else { $null }
    if ($operation) {
        if ($operation.Warning) {
            Write-Warning $operation.Warning
        }
        return [pscustomobject]@{ Order = $Job.Item.Order; Record = $operation.Result }
    }

    $message = 'Cache build failed.'
    Write-Warning ("Failed to cache {0}: {1}" -f $Job.Item.Name, $message)
    return ConvertTo-OrderedCacheBuildRecord -Order $Job.Item.Order -Name $Job.Item.Name -ScriptPath $Job.Item.Path -Status 'Failed' -Message $message -CacheExists $false -ExitCode $null
}

function Receive-CacheBuildWorkerBatch {
    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param([Parameter(Mandatory)][AllowEmptyCollection()][System.Collections.Generic.List[pscustomobject]]$JobList)

    $results = [System.Collections.Generic.List[pscustomobject]]::new()
    $completedJobs = [System.Collections.Generic.List[pscustomobject]]::new()
    foreach ($job in $JobList.ToArray()) {
        if (-not $job.Async.IsCompleted) { continue }
        try {
            [void]$results.Add((Receive-CompletedCacheBuildWorker -Job $job))
        }
        catch {
            $message = $_.Exception.Message
            Write-Warning ("Failed to cache {0}: {1}" -f $job.Item.Name, $message)
            [void]$results.Add((ConvertTo-OrderedCacheBuildRecord -Order $job.Item.Order -Name $job.Item.Name -ScriptPath $job.Item.Path -Status 'Failed' -Message $message -CacheExists $false -ExitCode $null -StdErr $message))
        }
        finally {
            $job.PowerShell.Dispose()
            [void]$completedJobs.Add($job)
        }
    }
    foreach ($job in $completedJobs) { [void]$JobList.Remove($job) }
    return [pscustomobject]@{ Results = $results.ToArray(); CompletedCount = $completedJobs.Count }
}

function Write-ParallelCacheBuildProgress {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][int]$Completed,
        [Parameter(Mandatory)][int]$Total,
        [Parameter(Mandatory)][int]$ActiveCount,
        [Parameter(Mandatory)][bool]$Quiet
    )

    if ($Quiet) { return }
    $status = "Building {0} of {1}" -f $Completed, $Total
    if ($ActiveCount -gt 0) { $status += " (active {0})" -f $ActiveCount }
    $percent = if ($Total -le 0) { 0 } else { [math]::Min(100, [math]::Max(0, ($Completed / $Total) * 100)) }
    Write-Progress -Id 2 -Activity 'Building colorscript cache' -Status $status -PercentComplete $percent
}

function Invoke-ParallelCacheBuildWork {
    [CmdletBinding()]
    [OutputType([object[]])]
    param(
        [Parameter(Mandatory)][AllowEmptyCollection()][System.Collections.Generic.List[pscustomobject]]$WorkQueue,
        [Parameter(Mandatory)][ValidateRange(1, 256)][int]$ThrottleLimit,
        [Parameter(Mandatory)][bool]$Force,
        [Parameter(Mandatory)][bool]$Quiet
    )

    if ($WorkQueue.Count -eq 0) { return @() }
    Write-ParallelCacheBuildProgress -Completed 0 -Total $WorkQueue.Count -ActiveCount 0 -Quiet $Quiet
    $moduleManifest = Join-Path -Path $script:ModuleRoot -ChildPath 'ColorScripts-Enhanced.psd1'
    $runspacePool = Initialize-CacheBuildRunspacePool -ThrottleLimit $ThrottleLimit -ModuleManifest $moduleManifest
    $workerScriptBlock = Get-CacheBuildWorkerScriptBlock
    $jobList = [System.Collections.Generic.List[pscustomobject]]::new()
    $results = [System.Collections.Generic.List[pscustomobject]]::new()
    $nextWorkIndex = 0
    $completed = 0
    try {
        while ($completed -lt $WorkQueue.Count) {
            $start = Start-CacheBuildWorkerBatch -WorkQueue $WorkQueue -JobList $jobList -NextWorkIndex $nextWorkIndex -ThrottleLimit $ThrottleLimit -RunspacePool $runspacePool -WorkerScriptBlock $workerScriptBlock -Force $Force -ModuleManifest $moduleManifest
            $nextWorkIndex = $start.NextWorkIndex
            foreach ($failure in $start.Failures) { [void]$results.Add($failure) }
            $completed += $start.Failures.Count
            $received = Receive-CacheBuildWorkerBatch -JobList $jobList
            foreach ($result in $received.Results) { [void]$results.Add($result) }
            $completed += $received.CompletedCount
            Write-ParallelCacheBuildProgress -Completed $completed -Total $WorkQueue.Count -ActiveCount $jobList.Count -Quiet $Quiet
            if ($received.CompletedCount -eq 0 -and $start.Failures.Count -eq 0) { Start-Sleep -Milliseconds 30 }
        }
    }
    finally {
        $runspacePool.Close()
        $runspacePool.Dispose()
    }
    if (-not $Quiet) { Write-Progress -Id 2 -Activity 'Building colorscript cache' -Completed -Status 'Completed' }
    return $results.ToArray()
}

function Get-CacheBuildSummary {
    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param([Parameter(Mandatory)][AllowEmptyCollection()][object[]]$Record)

    return [pscustomobject]@{
        Processed = $Record.Count
        Updated = @($Record | Where-Object Status -EQ 'Updated').Count
        Failed = @($Record | Where-Object Status -EQ 'Failed').Count
        Skipped = @($Record | Where-Object Status -Like 'Skipped*').Count
    }
}

function Write-CacheBuildActivityMarker {
    [CmdletBinding()]
    param([Parameter(Mandatory)][pscustomobject]$Summary)

    if ($Summary.Updated -le 0 -or -not $script:CacheDir) { return }
    try {
        $metadataFileName = 'cache-metadata-v{0}.json' -f $script:CacheFormatVersion
        Write-CacheMetadataFile -CacheDirectory $script:CacheDir -MetadataFileName $metadataFileName
    }
    catch { Write-Verbose ("Cache metadata update failed: {0}" -f $_.Exception.Message) }
}

function Write-CacheBuildSummary {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][pscustomobject]$Summary,
        [Parameter(Mandatory)][bool]$Quiet,
        [Parameter(Mandatory)][bool]$NoAnsiOutput
    )

    $formatString = if ($script:Messages -and $script:Messages.ContainsKey('CacheBuildSummaryFormat')) { $script:Messages.CacheBuildSummaryFormat } else { 'Cache build summary: Processed {0}, Updated {1}, Skipped {2}, Failed {3}' }
    if ([string]::IsNullOrWhiteSpace($formatString)) { $formatString = 'Cache build summary: Processed {0}, Updated {1}, Skipped {2}, Failed {3}' }
    $directoryFormat = if ($script:Messages -and $script:Messages.ContainsKey('CacheDirectoryFormat')) { $script:Messages.CacheDirectoryFormat } else { 'Cache directory: {0}' }
    $message = ($formatString -f $Summary.Processed, $Summary.Updated, $Summary.Skipped, $Summary.Failed) + [Environment]::NewLine + ($directoryFormat -f $script:CacheDir)
    $segment = New-ColorScriptAnsiText -Text $message -Color 'Cyan' -NoAnsiOutput:$NoAnsiOutput
    Write-ColorScriptInformation -Message $segment -Quiet:$Quiet -NoAnsiOutput:$NoAnsiOutput -PreferConsole:(-not $NoAnsiOutput) -Color 'Cyan'
}

function Invoke-ColorScriptCacheBuildRequest {
    [CmdletBinding()]
    [OutputType([object[]])]
    param(
        [Parameter(Mandatory)][AllowEmptyCollection()][string[]]$RequestedName,
        [Parameter()][AllowEmptyCollection()][string[]]$Category,
        [Parameter()][AllowEmptyCollection()][string[]]$Tag,
        [Parameter(Mandatory)][bool]$AllRequested,
        [Parameter(Mandatory)][bool]$Force,
        [Parameter(Mandatory)][bool]$PassThru,
        [Parameter(Mandatory)][bool]$ParallelRequested,
        [Parameter(Mandatory)][bool]$ThrottleLimitSpecified,
        [Parameter(Mandatory)][ValidateRange(0, 256)][int]$ThrottleLimit,
        [Parameter(Mandatory)][bool]$Quiet,
        [Parameter(Mandatory)][bool]$NoAnsiOutput,
        [Parameter(Mandatory)][System.Management.Automation.PSCmdlet]$Cmdlet
    )

    $candidateRecords = @(Get-CacheBuildCandidateRecord -RequestedName $RequestedName -Category $Category -Tag $Tag -AllRequested $AllRequested)
    if ($candidateRecords.Count -eq 0) {
        Write-Warning $script:Messages.NoScriptsSelectedForCacheBuild
        return @()
    }
    $metadataFilterRequested = ($Category -and $Category.Count -gt 0) -or ($Tag -and $Tag.Count -gt 0)
    $implicitPolicySelection = $RequestedName.Count -eq 0 -and -not $metadataFilterRequested
    $parallelPreference = Get-CacheBuildParallelPreference -ParallelRequested $ParallelRequested -ThrottleLimitSpecified $ThrottleLimitSpecified -ThrottleLimit $ThrottleLimit
    $orderedResults = if ($parallelPreference.UseParallel) {
        $plan = Get-ParallelCacheBuildPlan -CandidateRecord $candidateRecords -ImplicitPolicySelection $implicitPolicySelection -Force $Force -IncludeOutput $PassThru -Quiet $Quiet -Cmdlet $Cmdlet
        @($plan.Results) + @(Invoke-ParallelCacheBuildWork -WorkQueue $plan.WorkQueue -ThrottleLimit $parallelPreference.ThrottleLimit -Force $Force -Quiet $Quiet)
    }
    else {
        @(Invoke-SequentialCacheBuild -CandidateRecord $candidateRecords -ImplicitPolicySelection $implicitPolicySelection -Force $Force -IncludeOutput $PassThru -Quiet $Quiet -Cmdlet $Cmdlet)
    }
    $finalRecords = @($orderedResults | Sort-Object Order | ForEach-Object { $_.Record })
    $summary = Get-CacheBuildSummary -Record $finalRecords
    Write-CacheBuildActivityMarker -Summary $summary
    if (-not $PassThru -and $summary.Processed -gt 0) { Write-CacheBuildSummary -Summary $summary -Quiet $Quiet -NoAnsiOutput $NoAnsiOutput }
    if ($PassThru) { return $finalRecords }
    return @()
}

function New-ColorScriptCache {
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseOutputTypeCorrectly', '', Justification = 'Returns structured pipeline records for each cache operation.')]
    [OutputType([pscustomobject])]
    [CmdletBinding(DefaultParameterSetName = 'Selection', SupportsShouldProcess = $true, ConfirmImpact = 'Medium', HelpUri = 'https://nick2bad4u.github.io/PS-Color-Scripts-Enhanced/docs/help-redirect.html?cmdlet=New-ColorScriptCache')]
    [Alias('Update-ColorScriptCache', 'Build-ColorScriptCache')]
    param(
        [Parameter(ParameterSetName = 'Help')][Alias('help')][switch]$h,
        [Parameter(ParameterSetName = 'Selection', ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [SupportsWildcards()][ValidateScript({ Test-ColorScriptNameValue $_ -AllowWildcard })]
        [ArgumentCompleter({ param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) $null = $commandName, $parameterName, $commandAst, $fakeBoundParameters; Get-ColorScriptNameCompletion -WordToComplete $wordToComplete })]
        [string[]]$Name,
        [Parameter(ParameterSetName = 'All')][switch]$All,
        [Parameter(ParameterSetName = 'Selection')][Parameter(ParameterSetName = 'All')][switch]$Force,
        [Parameter(ParameterSetName = 'Selection')][Parameter(ParameterSetName = 'All')][switch]$PassThru,
        [Parameter(ParameterSetName = 'Selection')][Parameter(ParameterSetName = 'All')][string[]]$Category,
        [Parameter(ParameterSetName = 'Selection')][Parameter(ParameterSetName = 'All')][string[]]$Tag,
        [Parameter(ParameterSetName = 'Selection')][Parameter(ParameterSetName = 'All')][switch]$Parallel,
        [Parameter(ParameterSetName = 'Selection')][Parameter(ParameterSetName = 'All')][Alias('Threads')][ValidateRange(1, 256)][int]$ThrottleLimit,
        [Parameter(ParameterSetName = 'Selection')][Parameter(ParameterSetName = 'All')][switch]$Quiet,
        [Parameter(ParameterSetName = 'Selection')][Parameter(ParameterSetName = 'All')][Alias('NoColor')][switch]$NoAnsiOutput,
        [Parameter(ParameterSetName = 'Selection')][Parameter(ParameterSetName = 'All')][switch]$IncludePokemon
    )

    begin {
        $helpRequested = $h.IsPresent
        $nameSet = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
        $collectedNames = [System.Collections.Generic.List[string]]::new()
        $null = $IncludePokemon
        if ($helpRequested) {
            Show-ColorScriptHelp -CommandName 'New-ColorScriptCache'
            return
        }
        if ($PSBoundParameters.ContainsKey('All') -and -not $All) {
            Invoke-ColorScriptError -Message $script:Messages.SpecifyNameToSelectScripts -ErrorId 'ColorScriptsEnhanced.CacheSelectionMissing' -Category ([System.Management.Automation.ErrorCategory]::InvalidOperation) -Cmdlet $PSCmdlet
        }
        Initialize-CacheDirectory -ReadOnly:$WhatIfPreference
        foreach ($value in @($Name)) { Add-UniqueCacheBuildName -NameSet $nameSet -NameList $collectedNames -Value $value }
    }

    process {
        if ($helpRequested -or -not $MyInvocation.ExpectingInput) { return }
        if ($PSBoundParameters.ContainsKey('Name') -and $Name) {
            foreach ($value in $Name) { Add-UniqueCacheBuildName -NameSet $nameSet -NameList $collectedNames -Value $value }
            return
        }
        Add-UniqueCacheBuildName -NameSet $nameSet -NameList $collectedNames -Value (Get-CacheBuildPipelineName -InputObject $_)
    }

    end {
        if ($helpRequested) { return }
        Invoke-ColorScriptCacheBuildRequest -RequestedName $collectedNames.ToArray() -Category $Category -Tag $Tag -AllRequested $All.IsPresent -Force $Force.IsPresent -PassThru $PassThru.IsPresent -ParallelRequested $Parallel.IsPresent -ThrottleLimitSpecified $PSBoundParameters.ContainsKey('ThrottleLimit') -ThrottleLimit $ThrottleLimit -Quiet $Quiet.IsPresent -NoAnsiOutput $NoAnsiOutput.IsPresent -Cmdlet $PSCmdlet
    }
}