src/Private.Core.ps1

Set-StrictMode -Version Latest

$script:PSLRMRequirementsFileName = 'psreq.psd1'
$script:PSLRMLockfileFileName = 'psreq.lock.psd1'
$script:PSLRMStoreDirectoryName = '.pslrm'
$script:PSLRMResourceTypeName = 'PSLRM.Resource'

# Internal helpers

function Get-RequirementsPath {
    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string] $ProjectRoot
    )

    Join-Path $ProjectRoot $script:PSLRMRequirementsFileName
}

function Get-LockfilePath {
    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string] $ProjectRoot
    )

    Join-Path $ProjectRoot $script:PSLRMLockfileFileName
}

function Get-StorePath {
    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string] $ProjectRoot
    )

    Join-Path $ProjectRoot $script:PSLRMStoreDirectoryName
}

function ConvertTo-NormalizedVersionString {
    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter()]
        [AllowNull()]
        [object] $Version,

        [Parameter()]
        [AllowNull()]
        [string] $Prerelease
    )

    if ($null -eq $Version) {
        return $null
    }

    $normalized = if ($Version -is [string]) { $Version } else { $Version.ToString() }
    if ([string]::IsNullOrWhiteSpace($normalized)) {
        return $null
    }

    $normalized = $normalized.Trim()

    # NOTE: PSResourceGet reports prerelease separately (Version + Prerelease). Preserve it.
    if (-not [string]::IsNullOrWhiteSpace($Prerelease)) {
        $pr = $Prerelease.Trim()
        if (-not [string]::IsNullOrWhiteSpace($pr)) {
            if ($normalized -notmatch '-') {
                return "$normalized-$pr"
            }
        }
    }

    $normalized
}

function ConvertTo-PowerShellDataFileLiteral {
    [OutputType([string])]
    param(
        [Parameter(Mandatory)]
        [AllowNull()]
        [object] $Value,

        [Parameter(Mandatory)]
        [ValidateRange(0, 16)]
        [int] $IndentLevel,

        [Parameter(Mandatory)]
        [ValidateRange(2, 4)]
        [int] $IndentWidth
    )

    switch ($true) {
        ($null -eq $Value) {
            return '$null'
        }
        ($Value -is [hashtable]) {
            $lines = [System.Collections.Generic.List[string]]::new()
            $lines.Add('@{')

            $keys = [string[]]$Value.Keys
            [System.Array]::Sort($keys, [System.StringComparer]::Ordinal)
            $entryIndent = ' ' * ($IndentWidth * ($IndentLevel + 1))
            $closeIndent = ' ' * ($IndentWidth * $IndentLevel)
            foreach ($key in $keys) {
                $escapedKey = $key -replace "'", "''"
                $keyLiteral = "'$escapedKey'"

                $valLiteral = ConvertTo-PowerShellDataFileLiteral -Value $Value[$key] -IndentLevel ($IndentLevel + 1) -IndentWidth $IndentWidth
                $lines.Add($entryIndent + "$keyLiteral = $valLiteral")
            }

            $lines.Add($closeIndent + '}')
            if ($IndentLevel -eq 0) {
                $lines.Add('')
            }
            return ($lines -join "`n")
        }
        ($Value -is [string]) {
            $escaped = $Value -replace "'", "''"
            return "'$escaped'"
        }
        ($Value -is [bool]) {
            if ($Value) {
                return '$true'
            }
            return '$false'
        }
        ($Value.GetType() -in @([byte], [int16], [int32], [int64], [uint16], [uint32], [uint64], [single], [double], [decimal])) {
            return [string]$Value
        }
        ($Value -is [version]) {
            return "'$($Value.ToString())'"
        }
        ($Value -is [array]) {
            $itemLiterals = [System.Collections.Generic.List[string]]::new()
            foreach ($item in $Value) {
                $itemLiterals.Add((ConvertTo-PowerShellDataFileLiteral -Value $item -IndentLevel ($IndentLevel + 1) -IndentWidth $IndentWidth))
            }
            return '@(' + ($itemLiterals -join ', ') + ')'
        }
        default {
            throw "Unsupported value type for PowerShell data file serialization: $($Value.GetType().FullName)"
        }
    }
}

function Write-PowerShellDataFile {
    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string] $Path,

        [Parameter(Mandatory)]
        [ValidateNotNull()]
        [hashtable] $Data,

        [Parameter()]
        [ValidateRange(2, 4)]
        [int] $IndentWidth = 4
    )

    $directory = Split-Path -Parent -Path $Path
    if ([string]::IsNullOrWhiteSpace($directory)) {
        $directory = (Get-Location).Path
    }

    if (-not (Test-Path -LiteralPath $directory)) {
        New-Item -ItemType Directory -Path $directory | Out-Null
    }

    $content = ConvertTo-PowerShellDataFileLiteral -Value $Data -IndentLevel 0 -IndentWidth $IndentWidth

    if (-not $PSCmdlet.ShouldProcess($Path, 'Write PowerShell data file')) {
        return
    }

    $tmp = [System.IO.Path]::Combine($directory, [System.IO.Path]::GetRandomFileName())
    $utf8NoBom = [System.Text.UTF8Encoding]::new($false)

    try {
        [System.IO.File]::WriteAllText($tmp, $content, $utf8NoBom)
        Move-Item -LiteralPath $tmp -Destination $Path -Force
    }
    finally {
        if (Test-Path -LiteralPath $tmp) {
            Remove-Item -LiteralPath $tmp -Force -ErrorAction SilentlyContinue
        }
    }
}

function Read-Lockfile {
    [CmdletBinding()]
    [OutputType([hashtable])]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript({ -not [string]::IsNullOrWhiteSpace($_) })]
        [string] $Path
    )

    if (-not (Test-Path -LiteralPath $Path)) {
        throw "Lockfile not found: $Path"
    }

    $data = Import-PowerShellDataFile -Path $Path
    if ($data -isnot [hashtable]) {
        throw "Lockfile must be a hashtable: $Path"
    }

    $data
}

function Write-Lockfile {
    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript({ -not [string]::IsNullOrWhiteSpace($_) })]
        [string] $Path,

        [Parameter(Mandatory)]
        [ValidateNotNull()]
        [hashtable] $Data
    )

    # NOTE: indent width is fixed to 4 for lockfile to ensure deterministic output.
    Write-PowerShellDataFile -Path $Path -Data $Data -IndentWidth 4
}

function Find-ProjectRoot {
    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [ValidateScript({
                -not [string]::IsNullOrWhiteSpace($_)
            })]
        [string] $Path = (Get-Location).Path
    )

    $cursor = $Path

    if (Test-Path -LiteralPath $cursor -PathType Leaf) {
        $cursor = Split-Path -Parent -Path $cursor
    }

    if (-not (Test-Path -LiteralPath $cursor -PathType Container)) {
        throw "Path not found or not a directory: $Path"
    }

    $cursor = (Resolve-Path -LiteralPath $cursor).Path

    while ($true) {
        $requirementsPath = Get-RequirementsPath -ProjectRoot $cursor
        if (Test-Path -LiteralPath $requirementsPath) {
            return $cursor
        }

        $parent = Split-Path -Parent -Path $cursor
        if ([string]::IsNullOrWhiteSpace($parent) -or ($parent -eq $cursor)) {
            break
        }
        $cursor = $parent
    }

    throw "Project root not found. Missing psreq.psd1 from: $Path"
}

function New-PSLRMResourceObject {
    [CmdletBinding()]
    [OutputType([object])]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string] $Name,

        [Parameter()]
        [AllowNull()]
        [string] $Version,

        [Parameter()]
        [AllowNull()]
        [string] $Repository,

        [Parameter(Mandatory)]
        [bool] $IsDirect,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string] $ProjectRoot
    )

    $resource = [pscustomobject][ordered]@{
        Name = $Name
        Version = $Version
        Repository = $Repository
        IsDirect = $IsDirect
        ProjectRoot = $ProjectRoot
    }

    $resource.PSObject.TypeNames.Insert(0, $script:PSLRMResourceTypeName)
    $resource
}

function New-Resource {
    [CmdletBinding()]
    [OutputType([object])]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string] $Name,

        [Parameter()]
        [AllowNull()]
        [object] $Version,

        [Parameter()]
        [AllowNull()]
        [string] $Prerelease,

        [Parameter()]
        [AllowNull()]
        [string] $Repository,

        [Parameter(Mandatory)]
        [bool] $IsDirect,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string] $ProjectRoot
    )

    $normalizedVersion = ConvertTo-NormalizedVersionString -Version $Version -Prerelease $Prerelease

    New-PSLRMResourceObject -Name $Name -Version $normalizedVersion -Repository $Repository -IsDirect $IsDirect -ProjectRoot $ProjectRoot
}

function Get-LockfileResourceNames {
    [CmdletBinding()]
    [OutputType([string[]])]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNull()]
        [hashtable] $LockData
    )

    $names = [string[]]$LockData.Keys
    [System.Array]::Sort($names, [System.StringComparer]::Ordinal)
    $names
}

function Test-PslrmParameterToken {
    [CmdletBinding()]
    [OutputType([bool])]
    param(
        [Parameter()]
        [AllowNull()]
        [object] $Argument
    )

    if ($Argument -isnot [string]) {
        return $false
    }

    if (($Argument.Length -le 1) -or -not $Argument.StartsWith('-')) {
        return $false
    }

    if ($Argument -match '^-{1,2}\d') {
        return $false
    }

    $Argument -match '^-{1,2}[A-Za-z_][A-Za-z0-9_-]*(?::(?:\$?true|\$?false))?$'
}

function Get-PslrmParameterTokenInfo {
    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string] $ParameterToken
    )

    if (-not (Test-PslrmParameterToken -Argument $ParameterToken)) {
        throw "Invalid parameter token: $ParameterToken"
    }

    $match = [regex]::Match(
        $ParameterToken,
        '^-{1,2}(?<name>[A-Za-z_][A-Za-z0-9_-]*)(?::(?<value>\$?true|\$?false))?$'
    )

    $hasInlineBooleanValue = $match.Groups['value'].Success
    $inlineBooleanValue = $null
    if ($hasInlineBooleanValue) {
        $inlineBooleanValue = $match.Groups['value'].Value.TrimStart('$') -ieq 'true'
    }

    [pscustomobject]@{
        Name = $match.Groups['name'].Value
        HasInlineBooleanValue = $hasInlineBooleanValue
        InlineBooleanValue = $inlineBooleanValue
    }
}

function Get-PslrmCommandParameter {
    [CmdletBinding()]
    [OutputType([System.Management.Automation.ParameterMetadata])]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNull()]
        [System.Management.Automation.CommandInfo] $Command,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string] $ParameterToken
    )

    $parameterTokenInfo = Get-PslrmParameterTokenInfo -ParameterToken $ParameterToken
    $parameterName = $parameterTokenInfo.Name

    foreach ($candidateName in $Command.Parameters.Keys) {
        if ($candidateName -ieq $parameterName) {
            return $Command.Parameters[$candidateName]
        }
    }

    $null
}

function Invoke-PslrmCommandWithArgumentTokens {
    [CmdletBinding()]
    [OutputType([object])]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNull()]
        [System.Management.Automation.CommandInfo] $Command,

        [Parameter()]
        [AllowNull()]
        [object[]] $ArgumentTokens
    )

    if ($null -eq $ArgumentTokens) {
        $ArgumentTokens = @()
    }

    $commandVariableName = '__pslrmCmd'
    Set-Variable -Name $commandVariableName -Value $Command.Name

    $scriptParts = [System.Collections.Generic.List[string]]::new()
    $scriptParts.Add("& `$$commandVariableName")

    $expectValue = $false
    $argumentVariableIndex = 0

    for ($index = 0; $index -lt $ArgumentTokens.Count; $index++) {
        $argument = $ArgumentTokens[$index]

        if ($expectValue) {
            $valueVariableName = "__pslrmArg$argumentVariableIndex"
            $argumentVariableIndex++
            Set-Variable -Name $valueVariableName -Value $argument
            $scriptParts.Add("`$$valueVariableName")
            $expectValue = $false
            continue
        }

        $isParameterToken = Test-PslrmParameterToken -Argument $argument
        if ($isParameterToken) {
            $scriptParts.Add([string] $argument)

            $parameterTokenInfo = Get-PslrmParameterTokenInfo -ParameterToken $argument
            $parameter = Get-PslrmCommandParameter -Command $Command -ParameterToken $argument
            if ($null -ne $parameter) {
                if ($parameterTokenInfo.HasInlineBooleanValue) {
                    continue
                }

                $hasNext = ($index + 1) -lt $ArgumentTokens.Count
                if (-not $hasNext) {
                    $parameterType = $parameter.ParameterType
                    $isSwitch = ($parameterType -eq [System.Management.Automation.SwitchParameter]) -or ($parameterType -eq [bool])
                    if (-not $isSwitch) {
                        throw "Missing value for parameter '$argument'."
                    }
                }
                else {
                    $nextArgument = $ArgumentTokens[$index + 1]
                    $nextIsParameterToken = Test-PslrmParameterToken -Argument $nextArgument
                    $parameterType = $parameter.ParameterType
                    $isSwitch = ($parameterType -eq [System.Management.Automation.SwitchParameter]) -or ($parameterType -eq [bool])

                    if (-not $isSwitch) {
                        $expectValue = $true
                    }
                    elseif (-not $nextIsParameterToken) {
                        $expectValue = $true
                    }
                }
            }

            continue
        }

        $valueVariableName = "__pslrmArg$argumentVariableIndex"
        $argumentVariableIndex++
        Set-Variable -Name $valueVariableName -Value $argument
        $scriptParts.Add("`$$valueVariableName")
    }

    & ([scriptblock]::Create(($scriptParts -join ' ')))
}

function ConvertTo-InvocationArguments {
    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNull()]
        [System.Management.Automation.CommandInfo] $Command,

        [Parameter()]
        [AllowNull()]
        [object[]] $ArgumentTokens
    )

    $namedArguments = @{}
    $positionalArguments = [System.Collections.Generic.List[object]]::new()

    if ($null -eq $ArgumentTokens) {
        $ArgumentTokens = @()
    }

    for ($index = 0; $index -lt $ArgumentTokens.Count; $index++) {
        $argument = $ArgumentTokens[$index]

        if (Test-PslrmParameterToken -Argument $argument) {
            $parameterTokenInfo = Get-PslrmParameterTokenInfo -ParameterToken $argument
            $parameter = Get-PslrmCommandParameter -Command $Command -ParameterToken $argument

            if ($null -ne $parameter) {
                $parameterName = $parameter.Name
                $nextIndex = $index + 1
                $isSwitch = ($parameter.ParameterType -eq [System.Management.Automation.SwitchParameter]) -or ($parameter.ParameterType -eq [bool])
                $hasValue = $nextIndex -lt $ArgumentTokens.Count

                if ($parameterTokenInfo.HasInlineBooleanValue) {
                    $namedArguments[$parameterName] = $parameterTokenInfo.InlineBooleanValue
                    continue
                }

                if ($isSwitch) {
                    if ($hasValue -and -not (Test-PslrmParameterToken -Argument $ArgumentTokens[$nextIndex])) {
                        $namedArguments[$parameterName] = $ArgumentTokens[$nextIndex]
                        $index++
                    }
                    else {
                        $namedArguments[$parameterName] = $true
                    }
                }
                else {
                    if (-not $hasValue) {
                        throw "Missing value for parameter '$argument'."
                    }

                    $namedArguments[$parameterName] = $ArgumentTokens[$nextIndex]
                    $index++
                }

                continue
            }
        }

        $positionalArguments.Add($argument)
    }

    [pscustomobject]@{
        NamedArguments = $namedArguments
        PositionalArguments = $positionalArguments.ToArray()
    }
}

function Invoke-PslrmRunspaceCommand {
    [CmdletBinding()]
    [OutputType([object])]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string] $ProjectRoot,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string] $StorePath,

        [Parameter(Mandatory)]
        [ValidateNotNull()]
        [string[]] $ModuleNames,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string] $CommandName,

        [Parameter()]
        [AllowNull()]
        [object[]] $ArgumentTokens
    )

    Set-Location -LiteralPath $ProjectRoot

    $separator = [string][System.IO.Path]::PathSeparator
    $currentModulePath = [Environment]::GetEnvironmentVariable('PSModulePath', 'Process')
    $modulePathEntries = [System.Collections.Generic.List[string]]::new()
    $modulePathEntries.Add($StorePath)

    if (-not [string]::IsNullOrWhiteSpace($currentModulePath)) {
        foreach ($entry in ($currentModulePath -split [regex]::Escape($separator))) {
            if ([string]::IsNullOrWhiteSpace($entry)) {
                continue
            }

            if (-not $modulePathEntries.Contains($entry)) {
                $modulePathEntries.Add($entry)
            }
        }
    }

    [Environment]::SetEnvironmentVariable('PSModulePath', ($modulePathEntries.ToArray() -join $separator), 'Process')

    $importedModuleNames = [System.Collections.Generic.List[string]]::new()
    $missingModuleNames = [System.Collections.Generic.List[string]]::new()

    foreach ($moduleName in $ModuleNames) {
        $availableModules = @(Get-Module -ListAvailable -Name $moduleName)
        if ($availableModules.Count -eq 0) {
            $missingModuleNames.Add($moduleName)
            continue
        }

        $selectedModule = $availableModules | Sort-Object Version -Descending | Select-Object -First 1
        $importedModule = Import-Module -Name $selectedModule.Path -Force -PassThru
        $importedModuleNames.Add($importedModule.Name)
    }

    if ($missingModuleNames.Count -gt 0) {
        throw "Local resources missing from store: $($missingModuleNames.ToArray() -join ', '). Run Restore-PSLResource."
    }

    $commands = @(Get-Command -Name $CommandName -Module $importedModuleNames.ToArray() -All -ErrorAction SilentlyContinue)
    $candidateModuleNames = @(
        $commands |
            Where-Object { -not [string]::IsNullOrWhiteSpace($_.Source) } |
            ForEach-Object Source |
            Sort-Object -Unique
    )

    if ($candidateModuleNames.Count -eq 0) {
        throw "Command '$CommandName' was not found in local resources: $($importedModuleNames.ToArray() -join ', ')."
    }

    if ($candidateModuleNames.Count -gt 1) {
        throw "Command '$CommandName' is exported by multiple local resources: $($candidateModuleNames -join ', ')."
    }

    Invoke-PslrmCommandWithArgumentTokens -Command @($commands)[0] -ArgumentTokens $ArgumentTokens
}

function New-DataAddedSubscription {
    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNull()]
        [object] $Collection,

        [Parameter(Mandatory)]
        [ValidateNotNull()]
        [object] $Queue
    )

    $handler = [System.EventHandler[System.Management.Automation.DataAddedEventArgs]]({
            $null = $Queue.Enqueue($args[0][$args[1].Index])
        }.GetNewClosure())

    $Collection.add_DataAdded($handler)

    [pscustomobject]@{
        Collection = $Collection
        Handler = $handler
    }
}

function Remove-DataAddedSubscription {
    [CmdletBinding()]
    param(
        [Parameter()]
        [AllowNull()]
        [pscustomobject] $Subscription
    )

    if ($null -eq $Subscription) {
        return
    }

    if (($null -ne $Subscription.Collection) -and ($null -ne $Subscription.Handler)) {
        $Subscription.Collection.remove_DataAdded($Subscription.Handler)
    }
}

function Invoke-QueuedStreamDrain {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNull()]
        [pscustomobject[]] $Forwarders,

        [Parameter(Mandatory)]
        [ValidateNotNull()]
        [object] $ErrorQueue,

        [Parameter(Mandatory)]
        [ValidateNotNull()]
        [object] $ErrorRecords
    )

    $typedErrorRecords = [System.Collections.Generic.List[System.Management.Automation.ErrorRecord]]$ErrorRecords

    do {
        $drainedAny = $false

        foreach ($forwarder in $Forwarders) {
            $record = $null

            while ($forwarder.Queue.TryDequeue([ref]$record)) {
                $drainedAny = $true
                & $forwarder.Action $record
                $record = $null
            }
        }

        $errorRecord = $null
        while ($ErrorQueue.TryDequeue([ref]$errorRecord)) {
            $drainedAny = $true
            $typedErrorRecords.Add($errorRecord) | Out-Null
            $errorRecord = $null
        }
    } while ($drainedAny)
}

function Resolve-InvocationError {
    [CmdletBinding()]
    [OutputType([System.Management.Automation.ErrorRecord])]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNull()]
        [System.Management.Automation.ErrorRecord] $ErrorRecord
    )

    $currentException = $ErrorRecord.Exception
    $resolvedErrorRecord = $ErrorRecord

    while ($null -ne $currentException) {
        $errorRecordProperty = $currentException.PSObject.Properties['ErrorRecord']
        if ($null -ne $errorRecordProperty) {
            $innerErrorRecord = $errorRecordProperty.Value
            if (($innerErrorRecord -is [System.Management.Automation.ErrorRecord]) -and (-not [object]::ReferenceEquals($innerErrorRecord, $ErrorRecord))) {
                $resolvedErrorRecord = $innerErrorRecord
            }
        }

        $currentException = $currentException.InnerException
    }

    $resolvedErrorRecord
}

function Invoke-InIsolatedRunspace {
    [CmdletBinding()]
    [OutputType([object])]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string] $ProjectRoot,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string] $CommandName,

        [Parameter()]
        [AllowNull()]
        [object[]] $ArgumentTokens
    )

    $lockfilePath = Get-LockfilePath -ProjectRoot $ProjectRoot
    $storePath = Get-StorePath -ProjectRoot $ProjectRoot

    if (-not (Test-Path -LiteralPath $storePath -PathType Container)) {
        throw "Store path not found. Run Restore-PSLResource to recreate local resources: $storePath"
    }

    $lockData = Read-Lockfile -Path $lockfilePath
    $moduleNames = [string[]]@(Get-LockfileResourceNames -LockData $lockData)
    if ($moduleNames.Count -eq 0) {
        throw "Lockfile does not contain any local resources: $lockfilePath"
    }

    if ($null -eq $ArgumentTokens) {
        $ArgumentTokens = @()
    }

    # NOTE: Top-level isolated invocations need the caller host so host-aware output survives the
    # async BeginInvoke forwarding path. Nested isolated invocations must not reuse the current
    # runspace host, because propagating child hosts destabilizes nested build/test flows.
    # Track nesting depth so only depth 0 reuses $Host and deeper isolated runspaces fall back to
    # the default host created from InitialSessionState.
    #
    # NOTE: PSHOST-tagged InformationRecords need special handling when depth 0 shares $Host.
    # With a shared host, Write-Host is already rendered directly by that host. If PSLRM also
    # forwards the same PSHOST record from Streams.Information, the caller sees the same host
    # message twice. Nested isolated runspaces do not share the original caller host, so their
    # PSHOST records still need normal information-stream forwarding.
    $isolatedRunspaceDepthVariableName = 'PSLRMIsolatedRunspaceDepth'
    $isolatedRunspaceDepthVariable = $ExecutionContext.SessionState.PSVariable.Get($isolatedRunspaceDepthVariableName)
    $isolatedRunspaceDepth = if ($null -ne $isolatedRunspaceDepthVariable) {
        [int]$isolatedRunspaceDepthVariable.Value
    }
    else {
        0
    }

    $initialSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
    $runspaceFunctionNames = @(
        'Test-PslrmParameterToken'
        'Get-PslrmParameterTokenInfo'
        'Get-PslrmCommandParameter'
        'Invoke-PslrmCommandWithArgumentTokens'
        'Invoke-PslrmRunspaceCommand'
    )
    $initialSessionState.Variables.Add(
        [System.Management.Automation.Runspaces.SessionStateVariableEntry]::new(
            $isolatedRunspaceDepthVariableName,
            ($isolatedRunspaceDepth + 1),
            'Current nesting depth for PSLRM isolated runspaces.'
        )
    ) | Out-Null
    foreach ($functionName in $runspaceFunctionNames) {
        $functionDefinition = (Get-Command $functionName -CommandType Function).ScriptBlock.ToString()
        $initialSessionState.Commands.Add(
            [System.Management.Automation.Runspaces.SessionStateFunctionEntry]::new(
                $functionName,
                $functionDefinition
            )
        ) | Out-Null
    }

    $runspaceInvokerName = 'Invoke-PslrmRunspaceCommand'

    $shareCallerHost = $isolatedRunspaceDepth -eq 0

    $runspace = if ($shareCallerHost) {
        [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace($Host, $initialSessionState)
    }
    else {
        [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace($initialSessionState)
    }
    $runspace.Open()

    $outputCollection = $null
    $streamForwarders = @()
    $errorSubscription = $null

    try {
        $powerShell = [System.Management.Automation.PowerShell]::Create()
        $powerShell.Runspace = $runspace
        $powerShell = $powerShell.AddCommand($runspaceInvokerName)
        $powerShell = $powerShell.AddParameter('ProjectRoot', $ProjectRoot)
        $powerShell = $powerShell.AddParameter('StorePath', $storePath)
        $powerShell = $powerShell.AddParameter('ModuleNames', $moduleNames)
        $powerShell = $powerShell.AddParameter('CommandName', $CommandName)
        $powerShell = $powerShell.AddParameter('ArgumentTokens', $ArgumentTokens)

        $outputCollection = [System.Management.Automation.PSDataCollection[psobject]]::new()
        $inputCollection = [System.Management.Automation.PSDataCollection[psobject]]::new()
        $inputCollection.Complete()

        $streamForwarders = @(
            [pscustomobject]@{
                Collection = $outputCollection
                Queue = [System.Collections.Concurrent.ConcurrentQueue[object]]::new()
                Action = { param($record) $PSCmdlet.WriteObject($record) }
                Subscription = $null
            }
            [pscustomobject]@{
                Collection = $powerShell.Streams.Debug
                Queue = [System.Collections.Concurrent.ConcurrentQueue[System.Management.Automation.DebugRecord]]::new()
                Action = { param($record) $PSCmdlet.WriteDebug($record.Message) }
                Subscription = $null
            }
            [pscustomobject]@{
                Collection = $powerShell.Streams.Verbose
                Queue = [System.Collections.Concurrent.ConcurrentQueue[System.Management.Automation.VerboseRecord]]::new()
                Action = { param($record) $PSCmdlet.WriteVerbose($record.Message) }
                Subscription = $null
            }
            [pscustomobject]@{
                Collection = $powerShell.Streams.Warning
                Queue = [System.Collections.Concurrent.ConcurrentQueue[System.Management.Automation.WarningRecord]]::new()
                Action = { param($record) $PSCmdlet.WriteWarning($record.Message) }
                Subscription = $null
            }
            [pscustomobject]@{
                Collection = $powerShell.Streams.Information
                Queue = [System.Collections.Concurrent.ConcurrentQueue[System.Management.Automation.InformationRecord]]::new()
                Action = {
                    param($record)

                    $isHostRecord = ($null -ne $record.Tags) -and ($record.Tags -contains 'PSHOST')
                    # NOTE: Shared-host top-level invocations already rendered this host message.
                    # Re-forwarding the PSHOST record would duplicate the visible output.
                    if ($shareCallerHost -and $isHostRecord) {
                        return
                    }

                    $PSCmdlet.WriteInformation($record)
                }
                Subscription = $null
            }
            [pscustomobject]@{
                Collection = $powerShell.Streams.Progress
                Queue = [System.Collections.Concurrent.ConcurrentQueue[System.Management.Automation.ProgressRecord]]::new()
                Action = { param($record) $PSCmdlet.WriteProgress($record) }
                Subscription = $null
            }
        )

        $errorQueue = [System.Collections.Concurrent.ConcurrentQueue[System.Management.Automation.ErrorRecord]]::new()
        $errorRecords = [System.Collections.Generic.List[System.Management.Automation.ErrorRecord]]::new()

        foreach ($forwarder in $streamForwarders) {
            $forwarder.Subscription = New-DataAddedSubscription -Collection $forwarder.Collection -Queue $forwarder.Queue
        }

        $errorSubscription = New-DataAddedSubscription -Collection $powerShell.Streams.Error -Queue $errorQueue

        $drainQueues = {
            Invoke-QueuedStreamDrain -Forwarders $streamForwarders -ErrorQueue $errorQueue -ErrorRecords $errorRecords
        }

        $asyncResult = $null
        $endInvokeError = $null

        try {
            $asyncResult = $powerShell.BeginInvoke($inputCollection, $outputCollection)

            while (-not $asyncResult.AsyncWaitHandle.WaitOne(50)) {
                & $drainQueues
            }

            $powerShell.EndInvoke($asyncResult) | Out-Null
        }
        catch {
            $endInvokeError = Resolve-InvocationError -ErrorRecord $_
        }

        & $drainQueues

        if ($errorRecords.Count -gt 0) {
            $PSCmdlet.ThrowTerminatingError((Resolve-InvocationError -ErrorRecord $errorRecords[0]))
        }

        if ($null -ne $endInvokeError) {
            $PSCmdlet.ThrowTerminatingError($endInvokeError)
        }
    }
    finally {
        foreach ($forwarder in $streamForwarders) {
            Remove-DataAddedSubscription -Subscription $forwarder.Subscription
        }
        Remove-DataAddedSubscription -Subscription $errorSubscription
        if ($null -ne $powerShell) {
            $powerShell.Dispose()
        }
        if ($null -ne $runspace) {
            $runspace.Dispose()
        }
    }
}