PSModuleUpdate.psm1

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

# Source: src/public/Get-PSModuleUpdate.ps1
function Get-PSModuleUpdate {
    <#
    .EXTERNALHELP PSModuleUpdate-Help.xml
    .SYNOPSIS
    Creates an exact-version update plan without changing installed PowerShell modules.
    .DESCRIPTION
    Inspects a compatible installed module in CurrentUser or AllUsers and one exact repository.
    The target need not be loaded, be the caller, or depend on PSModuleUpdate.
    Policy defaults to Stable and CurrentUser. Credentials are used only in memory.
    An unregistered URI uses an ephemeral untrusted registration and nonsecret ownership receipt.
    Cleanup validates exact registration identity. An interrupted unbound receipt fails closed.
    .PARAMETER Module
    One installed module reference. Name and GUID must match its on-disk manifest.
    .PARAMETER Repository
    One literal registered PSResourceGet repository name.
    .PARAMETER RepositoryUri
    Exact endpoint. Reuses one matching registration or creates an owned temporary transport.
    .PARAMETER Policy
    Channel, Scope, ExpectedRepositoryUri (Uri), and VersionRange. Unknown keys are rejected.
    .PARAMETER Credential
    Optional per-call provider credential. Supply it again when installing.
    .EXAMPLE
    Get-PSModuleUpdate -Module (Get-Module ExampleModule) -Repository PSGallery
    Creates a stable CurrentUser update plan without changing installed files.
    .EXAMPLE
    Get-PSModuleUpdate -Module $installed -RepositoryUri ([uri]'https://api.nuget.org/v3/index.json')
    Uses only the specified endpoint. Packages must contain a valid PowerShell module manifest.
    .OUTPUTS
    PSModuleUpdate.UpdatePlan
    #>

    [CmdletBinding(DefaultParameterSetName = 'ByModule')]
    param(
        [Parameter(Mandatory)][System.Management.Automation.PSModuleInfo]$Module,
        [Parameter(Mandatory, ParameterSetName = 'ByModule')][ValidateNotNullOrEmpty()][string]$Repository,
        [Parameter(Mandatory, ParameterSetName = 'ByRepositoryUri')][ValidateNotNull()][uri]$RepositoryUri,
        [hashtable]$Policy = @{},
        [pscredential]$Credential
    )
    $selection = @{ Name = $Repository; Uri = $RepositoryUri }
    $context = Get-PlanningContext $Module $selection $Policy $Credential
    Register-UpdatePlan (ConvertTo-UpdatePlan $context) $context ([bool]$Credential)
}


# Source: src/public/Install-PSModuleUpdate.ps1
function Install-PSModuleUpdate {
    <#
    .EXTERNALHELP PSModuleUpdate-Help.xml
    .SYNOPSIS
    Applies one live exact-version module update plan.
    .DESCRIPTION
    Revalidates and stages the exact package without importing it. Same-base replacement uses
    a verified backup, durable journal, exclusive OS handle lock, and best-effort rollback.
    Only provider-generated root PSGetModuleInfo.xml is excluded from SHA256 tree comparison.
    No module is hot reloaded. Interrupted operations require safe recovery and a fresh plan.
    .PARAMETER Update
    An unmodified live PSModuleUpdate.UpdatePlan from this module instance; pipeline by value.
    .PARAMETER Credential
    Per-call provider credential; never persisted.
    .PARAMETER AcceptLicense
    Explicitly accepts provider package license requirements for this operation.
    .PARAMETER TrustRepository
    Allows this operation against an untrusted repository without changing its registration.
    .EXAMPLE
    $plan | Install-PSModuleUpdate -WhatIf
    Revalidates read-only without locking, staging, installing, or repairing recovery state.
    .EXAMPLE
    $plan | Install-PSModuleUpdate -AcceptLicense -TrustRepository
    Requests native confirmation before applying the exact approved target and scope.
    .OUTPUTS
    PSModuleUpdate.UpdateResult
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High', DefaultParameterSetName = 'ByUpdatePlan')]
    param(
        [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'ByUpdatePlan')][ValidateNotNull()][object]$Update,
        [Parameter(ParameterSetName = 'ByUpdatePlan')][pscredential]$Credential,
        [Parameter(ParameterSetName = 'ByUpdatePlan')][switch]$AcceptLicense,
        [Parameter(ParameterSetName = 'ByUpdatePlan')][switch]$TrustRepository
    )
    process {
        $operation = Initialize-InstallationRequest $Update $Credential @{ AcceptLicense = [bool]$AcceptLicense; TrustRepository = [bool]$TrustRepository }
        $target = "$($Update.ModuleName) $($Update.TargetVersion) from $($Update.RepositoryName ?? $Update.RepositoryUri) into $($Update.Scope)"
        if ($PSCmdlet.ShouldProcess($target, 'Install exact module update')) { Invoke-UpdateInstallation $operation }
        else { ConvertTo-DeclinedUpdateResult $Update }
    }
}


# Source: src/private/identity/Assert-ManifestRootModule.ps1
function Assert-ManifestRootModule {
    param([hashtable]$Data, [string]$ManifestPath)
    if (-not $Data['RootModule']) { return }
    $root = Split-Path -Parent $ManifestPath
    $path = Assert-SafePath (Join-Path $root $Data['RootModule'])
    $relative = [IO.Path]::GetRelativePath($root, $path)
    if ($relative.StartsWith('..') -or [IO.Path]::IsPathRooted($relative)) {
        Write-PMUError 'PMU.ModuleIdentityMismatch' 'The declared root module must remain inside the package directory.'
    }
    if (-not (Test-Path -LiteralPath $path -PathType Leaf)) {
        Write-PMUError 'PMU.ModuleIdentityMismatch' 'The declared root module is missing from the extracted package.'
    }
}


# Source: src/private/identity/Assert-ReferenceInstallationLayout.ps1
function Assert-ReferenceInstallationLayout {
    param([object]$Reference, [object[]]$Observed, [object]$RecoveryJournal)
    $installations = @($Observed | Where-Object ManifestPath -CEQ $Reference.ManifestPath)
    if ($installations.Count -eq 1) { return }
    Assert-BackupReferenceIdentity $Reference $RecoveryJournal
}


# Source: src/private/identity/Assert-SelectedInstallation.ps1
function Assert-SelectedInstallation {
    param([object[]]$Installations, [guid]$Guid)
    if (-not $Installations.Count) {
        Write-PMUError 'PMU.UnsupportedInstallation' 'No existing installation was found in the requested scope.'
    }
    if (@($Installations | Where-Object Guid -NE $Guid).Count) {
        Write-PMUError 'PMU.GuidMismatch' 'A selected-scope manifest has a different GUID.'
    }
}


# Source: src/private/identity/Get-ActiveModuleIdentity.ps1
function Get-ActiveModuleIdentity {
    param([System.Management.Automation.PSModuleInfo]$Module)
    $loaded = @(Get-Module -Name $Module.Name | Where-Object Guid -EQ $Module.Guid | Select-Object -First 1)
    if (-not $loaded.Count) { return $null }
    $active = $loaded[0]
    [pscustomobject]@{
        Name = $active.Name; Guid = $active.Guid; Version = Get-ActiveModuleVersion $active
        ManifestPath = Assert-SafePath (Join-Path $active.ModuleBase "$($active.Name).psd1")
    }
}


# Source: src/private/identity/Get-ActiveModuleVersion.ps1
function Get-ActiveModuleVersion {
    param([System.Management.Automation.PSModuleInfo]$Module)
    $full = "$($Module.Version)"
    $label = ''
    if ($Module.PrivateData -is [System.Collections.IDictionary]) {
        $privateData = $Module.PrivateData
        if ($privateData['PSData']) { $label = "$($privateData['PSData']['Prerelease'])" }
    }
    if ($label) { $full += "-$label" }
    ConvertTo-CompleteModuleVersion $full
}


# Source: src/private/identity/Get-InstalledModuleIdentityList.ps1
function Get-InstalledModuleIdentityList {
    param([string]$Name, [string]$Scope, [string]$ExcludedPath)
    $root = Resolve-ModuleInstallationRoot $Scope
    $directory = Assert-SafePath (Join-Path $root $Name)
    if (-not (Test-Path -LiteralPath $directory)) { return }
    foreach ($child in Get-ChildItem -LiteralPath $directory -Directory -Force) {
        if ($child.FullName -ceq $ExcludedPath) { continue }
        $identity = Get-ManifestIdentity (Join-Path $child.FullName "$Name.psd1")
        if ($child.Name -cne $identity.Version.NumericVersion) {
            Write-PMUError 'PMU.UnsupportedInstallation' 'Only provider numeric-version directory layouts are supported.'
        }
        [pscustomobject]@{ FullVersion = $identity.Version.FullVersion; NumericVersion = $identity.Version.NumericVersion; Prerelease = $identity.Version.Prerelease; Scope = $Scope; Root = $root; ManifestPath = $identity.ManifestPath; Guid = $identity.Guid }
    }
}


# Source: src/private/identity/Get-ManifestIdentity.ps1
function Get-ManifestIdentity {
    param([string]$Path)
    $safe = Assert-SafePath $Path
    try { $data = Import-PowerShellDataFile -LiteralPath $safe -ErrorAction Stop }
    catch { Write-PMUError 'PMU.ModuleIdentityMismatch' 'The module manifest cannot be safely read.' }
    if (-not $data['GUID'] -or -not $data['ModuleVersion']) {
        Write-PMUError 'PMU.ModuleIdentityMismatch' 'The module manifest must declare version and GUID.'
    }
    Assert-ManifestRootModule $data $safe
    $version = Get-ManifestModuleVersion $data
    [pscustomobject]@{ Name = [IO.Path]::GetFileNameWithoutExtension($safe); Guid = [guid]$data.GUID; Version = $version; ManifestPath = $safe }
}


# Source: src/private/identity/Get-ManifestModuleVersion.ps1
function Get-ManifestModuleVersion {
    param([hashtable]$Data)
    $label = ''
    if ($Data['PrivateData'] -and $Data['PrivateData']['PSData']) { $label = "$($Data['PrivateData']['PSData']['Prerelease'])" }
    $full = "$($Data['ModuleVersion'])"
    if ($label) { $full += "-$label" }
    ConvertTo-CompleteModuleVersion $full
}


# Source: src/private/identity/Get-ModuleReferenceIdentity.ps1
function Get-ModuleReferenceIdentity {
    param([System.Management.Automation.PSModuleInfo]$Module)
    if ($Module.Name -notmatch '^[A-Za-z0-9][A-Za-z0-9_.-]*$') { Write-PMUError 'PMU.InvalidRequest' 'A literal module identity is required.' }
    $identity = Get-ManifestIdentity (Join-Path $Module.ModuleBase "$($Module.Name).psd1")
    if ($identity.Guid -ne $Module.Guid -or [version]$identity.Version.NumericVersion -ne $Module.Version) {
        Write-PMUError 'PMU.ModuleIdentityMismatch' 'The supplied module reference differs from its manifest.'
    }
    $identity.Version = Get-ActiveModuleVersion $Module
    $identity
}


# Source: src/private/identity/Resolve-ModuleInstallationRoot.ps1
function Resolve-ModuleInstallationRoot {
    param([ValidateSet('CurrentUser', 'AllUsers')][string]$Scope)
    if ($IsWindows) {
        if ($Scope -eq 'AllUsers') { return Assert-SafePath (Join-Path $env:ProgramFiles 'PowerShell/Modules') }
        return Assert-SafePath (Join-Path ([Environment]::GetFolderPath('MyDocuments')) 'PowerShell/Modules')
    }
    if ($Scope -eq 'AllUsers') { return Assert-SafePath '/usr/local/share/powershell/Modules' }
    Assert-SafePath (Join-Path $HOME '.local/share/powershell/Modules')
}


# Source: src/private/installation/Assert-ActivePlanIdentity.ps1
function Assert-ActivePlanIdentity {
    param([object]$Operation)
    $module = $Operation.Record.Module
    $path = Assert-SafePath (Join-Path $module.ModuleBase "$($module.Name).psd1")
    if ($path -cne $Operation.Record.ReferencePath -or $module.Guid -ne $Operation.Plan.ModuleGuid) {
        Write-PMUError 'PMU.PlanStale' 'The original active module identity or path changed.'
    }
    Assert-LoadedPlanIdentity $Operation
}


# Source: src/private/installation/Assert-ExactTarget.ps1
function Assert-ExactTarget {
    param([object]$Operation)
    $plan = $Operation.Plan
    Assert-RepositoryBinding $plan $Operation.RepositoryName
    $request = @{ Name = $plan.ModuleName; Repository = Get-OperationRepositoryName $Operation; Version = "[$($plan.TargetVersion)]"; Prerelease = $plan.TargetIsPrerelease }
    $resources = @(Find-ProviderResource $request $Operation.Credential | ForEach-Object { ConvertTo-ProviderRelease $_ })
    $exact = @($resources | Where-Object { $_.Name -ceq $plan.ModuleName -and $_.Version.FullVersion -ceq $plan.TargetVersion })
    if ($exact.Count -ne 1) { Write-PMUError 'PMU.PackageNotFound' 'The exact approved target is no longer uniquely available.' }
    $Operation.RequiresLicenseAcceptance = $exact[0].RequiresLicenseAcceptance
}


# Source: src/private/installation/Assert-InstallationConsent.ps1
function Assert-InstallationConsent {
    param([object]$Operation)
    Assert-RequiredConsent $Operation.Record.RequiresCredential ([bool]$Operation.Credential) 'PMU.AuthenticationFailed' 'Supply the direct credential again; PMU does not retain credentials between calls.'
    Assert-RequiredConsent $Operation.Plan.RequiresRepositoryTrust $Operation.Consent.TrustRepository 'PMU.RepositoryTrustRequired' 'Explicit -TrustRepository is required for this operation.'
    Assert-RequiredConsent ($Operation.Plan.RequiresLicenseAcceptance -or $Operation.RequiresLicenseAcceptance) $Operation.Consent.AcceptLicense 'PMU.LicenseAcceptanceRequired' 'Explicit -AcceptLicense is required when package license requirements are declared or undisclosed.'
}


# Source: src/private/installation/Assert-LoadedPlanIdentity.ps1
function Assert-LoadedPlanIdentity {
    param([object]$Operation)
    $active = Get-ActiveModuleIdentity $Operation.Record.Module
    $actual = @{ ActivePath = $null; ActiveVersion = $null }
    if ($active) { $actual = @{ ActivePath = $active.ManifestPath; ActiveVersion = $active.Version.FullVersion } }
    Assert-MatchingProperty $actual $Operation.Plan @('ActivePath', 'ActiveVersion') 'PMU.PlanStale'
}


# Source: src/private/installation/Assert-PlanEnvironment.ps1
function Assert-PlanEnvironment {
    param([object]$Operation)
    $plan = $Operation.Plan
    $provider = Get-ProviderContext
    Assert-RepositoryBinding $plan $Operation.RepositoryName
    $root = Resolve-ModuleInstallationRoot $plan.Scope
    if ($root -cne $plan.DestinationRoot -or "$($provider.Version)" -cne $plan.ProviderVersion) {
        Write-PMUError 'PMU.PlanStale' 'Destination or provider context changed after planning.'
    }
    Assert-ActivePlanIdentity $Operation
}


# Source: src/private/installation/Assert-RequiredConsent.ps1
function Assert-RequiredConsent {
    param([bool]$Required, [bool]$Allowed, [string]$ErrorId, [string]$Message)
    if ($Required -and -not $Allowed) { Write-PMUError $ErrorId $Message }
}


# Source: src/private/installation/Complete-VerifiedInstallation.ps1
function Complete-VerifiedInstallation {
    param([object]$Operation, [object[]]$Tree, [bool]$AlreadyInstalled)
    $outcome = 'AlreadyInstalled'
    if ($AlreadyInstalled) { Assert-InstalledPackage $Operation $Tree }
    else {
        if (-not $Operation.Plan.UpdateAvailable) { Write-PMUError 'PMU.PlanStale' 'This plan does not authorize a newer target installation.' }
        Initialize-Replacement $Operation $Tree
        Invoke-ProviderInstallation $Operation $Tree
        $outcome = 'Installed'
    }
    $result = ConvertTo-UpdateResult $Operation.Plan $outcome 'Verified'
    $result.RecoveryStatus = $Operation.RecoveryStatus
    Clear-CompletedOperation $Operation
    $result
}


# Source: src/private/installation/ConvertTo-DeclinedUpdateResult.ps1
function ConvertTo-DeclinedUpdateResult {
    param([object]$Plan)
    $outcome = 'Cancelled'
    if ($WhatIfPreference) { $outcome = 'WhatIf' }
    ConvertTo-UpdateResult $Plan $outcome
}


# Source: src/private/installation/ConvertTo-InstallationOperation.ps1
function ConvertTo-InstallationOperation {
    param([object]$Plan, [object]$Record, [hashtable]$Consent, [pscredential]$Credential)
    $dataRoot = Resolve-PMUDataRoot $Plan.Scope
    $work = Join-Path $dataRoot (Get-InstallationLockIdentity $Plan)
    [pscustomobject]@{
        Plan = $Plan; Record = $Record; Consent = $Consent; Credential = $Credential
        RepositoryName = $null
        DataRoot = $dataRoot; WorkRoot = $work; LockPath = Join-Path $work 'operation.lock'
        JournalPath = Join-Path $work 'journal.json'; StageRoot = Join-Path $work 'stage'
        BackupPath = Join-Path $work 'backup'; Reinstall = $false; Journal = $null
        StagedPath = Join-Path $work "stage/$($Plan.ModuleName)/$($Plan.TargetNumericVersion)"
        RecoveryStatus = 'NotRequired'
        RequiresLicenseAcceptance = $false
    }
}


# Source: src/private/installation/ConvertTo-UpdateResult.ps1
function ConvertTo-UpdateResult {
    param([object]$Plan, [string]$Outcome, [string]$VerificationStatus = 'NotPerformed')
    $installed = Get-ResultInstallationState $Plan $VerificationStatus
    [pscustomobject]@{
        PSTypeName = 'PSModuleUpdate.UpdateResult'; SchemaVersion = 1; Outcome = $Outcome
        ModuleName = $Plan.ModuleName; ModuleGuid = $Plan.ModuleGuid; RepositoryName = $Plan.RepositoryName
        RepositoryUri = $Plan.RepositoryUri; Scope = $Plan.Scope; ActiveVersion = $Plan.ActiveVersion
        InstalledVersion = $installed.Version; TargetVersion = $Plan.TargetVersion; InstalledPath = $installed.Path
        VerificationStatus = $VerificationStatus; RestartRequired = (Test-RestartRequired $Plan $Outcome)
        DependencySummary = @($Plan.DependencySummary); RecoveryStatus = 'NotRequired'; RecoveryGuidance = ''
    }
}


# Source: src/private/installation/Get-ResultInstallationState.ps1
function Get-ResultInstallationState {
    param([object]$Plan, [string]$VerificationStatus)
    if ($VerificationStatus -ne 'Verified') {
        return @{ Version = $Plan.InstalledVersion; Path = Split-Path -Parent $Plan.InstalledManifestPath }
    }
    $identity = Get-ManifestIdentity (Join-Path $Plan.TargetInstalledPath "$($Plan.ModuleName).psd1")
    @{ Version = $identity.Version.FullVersion; Path = $Plan.TargetInstalledPath }
}


# Source: src/private/installation/Initialize-InstallationRequest.ps1
function Initialize-InstallationRequest {
    param([object]$Update, [pscredential]$Credential, [hashtable]$Consent)
    $record = Test-UpdatePlan $Update
    $operation = ConvertTo-InstallationOperation $Update $record $Consent $Credential
    Assert-PlanEnvironment $operation
    if ($Update.RepositoryName) { Assert-ExactTarget $operation }
    $journal = Read-RecoveryJournal $operation
    if (-not $journal -or $journal.State -in @('Completed', 'RecoveryCompleted')) {
        $null = Test-InstalledPlanState $operation
    }
    $operation
}


# Source: src/private/installation/Initialize-Replacement.ps1
function Initialize-Replacement {
    param([object]$Operation, [object[]]$Tree)
    $path = $Operation.Plan.TargetInstalledPath
    $Operation.Reinstall = Test-Path -LiteralPath $path
    if ($Operation.Reinstall) {
        $identity = Get-ManifestIdentity (Join-Path $path "$($Operation.Plan.ModuleName).psd1")
        if ($identity.Guid -ne $Operation.Plan.ModuleGuid) { Write-PMUError 'PMU.GuidMismatch' 'Refusing to replace a different GUID.' }
    }
    $Operation.Journal = ConvertTo-RecoveryJournal $Operation $Tree
    Write-RecoveryJournalState $Operation.Journal 'Preparation'
    if ($Operation.Reinstall) { Copy-InstallationBackup $Operation }
}


# Source: src/private/installation/Invoke-LockedInstallation.ps1
function Invoke-LockedInstallation {
    param([object]$Operation)
    Assert-PlanEnvironment $Operation
    Repair-UnfinishedInstallation $Operation
    $alreadyInstalled = Test-InstalledPlanState $Operation
    Clear-CompletedOperation $Operation
    Assert-ExactTarget $Operation
    Assert-InstallationConsent $Operation
    $tree = @(Initialize-StagedPackage $Operation)
    Complete-VerifiedInstallation $Operation $tree $alreadyInstalled
}


# Source: src/private/installation/Invoke-ProviderInstallation.ps1
function Invoke-ProviderInstallation {
    param([object]$Operation, [object[]]$StagedTree)
    Write-RecoveryJournalState $Operation.Journal 'ReplacementInProgress'
    try {
        Write-InstallationPhase $Operation.Plan 'Replacing'
        Assert-RepositoryBinding $Operation.Plan $Operation.RepositoryName
        Install-ProviderResource $Operation
        Write-RecoveryJournalState $Operation.Journal 'Verification'
        Write-InstallationPhase $Operation.Plan 'Verifying'
        Assert-InstalledPackage $Operation $StagedTree
        Write-RecoveryJournalState $Operation.Journal 'Completed'
    }
    catch { Resolve-InstallationFailure $Operation $_ }
}


# Source: src/private/installation/Invoke-RepositoryInstallation.ps1
function Invoke-RepositoryInstallation {
    param([object]$Operation)
    $plan = $Operation.Plan
    $source = [pscustomobject]@{ Name = $plan.RepositoryName; Uri = $plan.RepositoryUri; ApiVersion = $plan.RepositoryApiVersion; Trusted = $plan.RepositoryTrusted }
    $lease = Open-RepositoryTransport $source
    $Operation.RepositoryName = $lease.Name
    $failure = $null
    try { Invoke-ScopedInstallation $Operation }
    catch { $failure = $_; throw }
    finally { Close-RepositoryTransport $lease $failure }
}


# Source: src/private/installation/Invoke-ScopedInstallation.ps1
function Invoke-ScopedInstallation {
    param([object]$Operation)
    Initialize-ControlledDirectory $Operation.DataRoot
    Initialize-ControlledDirectory $Operation.WorkRoot
    $lock = Enter-InstallationLock $Operation.LockPath
    try { Invoke-LockedInstallation $Operation }
    finally { $lock.Dispose() }
}


# Source: src/private/installation/Invoke-UpdateInstallation.ps1
function Invoke-UpdateInstallation {
    param([object]$Operation)
    Assert-InstallationConsent $Operation
    Write-InstallationPhase $Operation.Plan 'Checking'
    try { Invoke-RepositoryInstallation $Operation }
    catch [UnauthorizedAccessException] { Write-PMUError 'PMU.PermissionDenied' 'The selected scope is not writable. PMU does not elevate or change scope.' }
    finally { Write-InstallationPhase $Operation.Plan 'Completed' }
}


# Source: src/private/installation/Resolve-InstallationFailure.ps1
function Resolve-InstallationFailure {
    param([object]$Operation, [System.Management.Automation.ErrorRecord]$Failure)
    try { Restore-InstallationBackup $Operation.Journal $Operation }
    catch {
        $Operation.Journal.RecoveryStatus = 'Failed'
        Write-RecoveryJournalState $Operation.Journal 'RecoveryRequired'
        Write-PMUError 'PMU.RollbackFailed' 'Installation and rollback both failed. Recoverable evidence was preserved.' 'Do not delete the PMU journal or backup; inspect the selected installation before obtaining a fresh plan.'
    }
    if ($Failure.FullyQualifiedErrorId -like 'PMU.*') { throw $Failure }
    Write-ProviderError $Failure 'PMU.InstallationFailed'
}


# Source: src/private/installation/Test-InstalledPlanState.ps1
function Test-InstalledPlanState {
    param([object]$Operation)
    $plan = $Operation.Plan
    $current = @(Get-InstalledModuleIdentityList $plan.ModuleName $plan.Scope)
    Assert-SelectedInstallation $current $plan.ModuleGuid
    $target = @($current | Where-Object FullVersion -CEQ $plan.TargetVersion)
    if ($target.Count -eq 1) { return $true }
    $original = @($plan.ObservedInstalledVersions | Where-Object Scope -EQ $plan.Scope)
    if ((ConvertTo-Json @($current) -Depth 5 -Compress) -cne (ConvertTo-Json @($original) -Depth 5 -Compress)) {
        Write-PMUError 'PMU.InstalledStateChanged' 'The selected-scope installation changed after planning.'
    }
    Assert-FileTreeEqual $Operation.Record.InstalledTree @(Get-FileTreeInventory (Split-Path -Parent $plan.InstalledManifestPath)) 'PMU.InstalledStateChanged'
    $false
}


# Source: src/private/installation/Test-RestartRequired.ps1
function Test-RestartRequired {
    param([object]$Plan, [string]$Outcome)
    $loaded = @(Get-Module -Name $Plan.ModuleName | Where-Object Guid -EQ $Plan.ModuleGuid)
    if (-not $loaded.Count) { return $false }
    $target = [IO.Path]::GetFullPath($Plan.TargetInstalledPath)
    $replacedLoaded = @($loaded | Where-Object { [IO.Path]::GetFullPath($_.ModuleBase) -eq $target }).Count -gt 0
    if ($Outcome -eq 'Installed' -and $replacedLoaded) { return $true }
    $behindTarget = @($loaded | Where-Object { (Compare-CompleteModuleVersion $Plan.TargetVersion (Get-ActiveModuleVersion $_).FullVersion) -gt 0 })
    $behindTarget.Count -gt 0
}


# Source: src/private/installation/Write-InstallationPhase.ps1
function Write-InstallationPhase {
    param([object]$Plan, [string]$Phase)
    if ([Console]::IsOutputRedirected) { return }
    if ([Environment]::GetCommandLineArgs() -contains '-NonInteractive') { return }
    $progressId = 17330 # Stable native progress activity for the single-module workflow.
    $activity = "Updating $($Plan.ModuleName)"
    if ($Phase -eq 'Completed') {
        Write-Progress -Id $progressId -Activity $activity -Completed
        return
    }
    Write-Progress -Id $progressId -Activity $activity -Status $Phase
}


# Source: src/private/locking/Assert-UnixDirectoryOwner.ps1
function Assert-UnixDirectoryOwner {
    param([string]$Path)
    $item = Get-Item -LiteralPath $Path -Force -ErrorAction Stop
    $owner = $item.PSObject.Properties['User']
    if (-not $owner) { Write-PMUError 'PMU.PermissionDenied' 'The data-directory owner cannot be verified on this platform.' }
    if ($owner.Value -cnotin @([Environment]::UserName, 'root')) {
        Write-PMUError 'PMU.PermissionDenied' 'The PMU data directory is owned by an unrelated account.'
    }
}


# Source: src/private/locking/Assert-WindowsAccessRule.ps1
function Assert-WindowsAccessRule {
    param([object]$Rule, [string[]]$Trusted)
    if (-not $Rule.Allowed) { return }
    if (-not $Rule.CanWrite) { return }
    if ($Rule.Identity -notin $Trusted) {
        Write-PMUError 'PMU.PermissionDenied' 'The PMU data directory grants write access to an unrelated account or group.'
    }
}


# Source: src/private/locking/Assert-WindowsDirectoryDescriptor.ps1
function Assert-WindowsDirectoryDescriptor {
    param([object]$Descriptor)
    if ($Descriptor.Owner -notin $Descriptor.Trusted) { Write-PMUError 'PMU.PermissionDenied' 'The PMU data-directory owner is not trusted.' }
    foreach ($rule in $Descriptor.Access) { Assert-WindowsAccessRule $rule $Descriptor.Trusted }
}


# Source: src/private/locking/ConvertTo-WindowsAccessRule.ps1
function ConvertTo-WindowsAccessRule {
    param([object]$Rule)
    $write = [Security.AccessControl.FileSystemRights]::Write -bor [Security.AccessControl.FileSystemRights]::Delete -bor [Security.AccessControl.FileSystemRights]::ChangePermissions -bor [Security.AccessControl.FileSystemRights]::TakeOwnership
    [pscustomobject]@{
        Identity = $Rule.IdentityReference.Value
        Allowed = $Rule.AccessControlType -eq [Security.AccessControl.AccessControlType]::Allow
        CanWrite = [bool]($Rule.FileSystemRights -band $write)
    }
}


# Source: src/private/locking/Enter-InstallationLock.ps1
function Enter-InstallationLock {
    param([string]$Path, [int]$TimeoutMilliseconds = 5000)
    $null = Assert-SafePath $Path
    $timer = [Diagnostics.Stopwatch]::StartNew()
    do {
        try { return [IO.File]::Open($Path, [IO.FileMode]::OpenOrCreate, [IO.FileAccess]::ReadWrite, [IO.FileShare]::None) }
        catch [UnauthorizedAccessException] { Write-PMUError 'PMU.PermissionDenied' 'Cannot open the scope-specific PMU lock.' }
        catch [IO.IOException] { Start-Sleep -Milliseconds 50 }
    } while ($timer.ElapsedMilliseconds -lt $TimeoutMilliseconds)
    Write-PMUError 'PMU.LockTimeout' 'Another process holds the module/scope/root lock. Retry after it completes.'
}


# Source: src/private/locking/Enter-RepositoryRegistryLock.ps1
function Enter-RepositoryRegistryLock {
    param([string]$Root, [int]$TimeoutMilliseconds = 5000)
    Initialize-ControlledDirectory $Root
    Enter-InstallationLock (Join-Path $Root 'registry.lock') $TimeoutMilliseconds
}


# Source: src/private/locking/Get-InstallationLockIdentity.ps1
function Get-InstallationLockIdentity {
    param([object]$Plan)
    $identity = "$($Plan.ModuleName.ToLowerInvariant())|$($Plan.Scope)|$($Plan.DestinationRoot)"
    if ($IsWindows -or $IsMacOS) { $identity = $identity.ToLowerInvariant() }
    $hash = [Security.Cryptography.SHA256]::HashData([Text.Encoding]::UTF8.GetBytes($identity))
    [Convert]::ToHexString($hash).ToLowerInvariant()
}


# Source: src/private/locking/Get-RepositoryProcessStart.ps1
function Get-RepositoryProcessStart {
    param([int]$ProcessId)
    try { $process = [Diagnostics.Process]::GetProcessById($ProcessId) }
    catch [ArgumentException] { return $null }
    try { "$($process.StartTime.ToUniversalTime().Ticks)" }
    catch { Write-PMUError 'PMU.RepositoryRecoveryRequired' 'Cannot verify the repository receipt owner process.' }
    finally { $process.Dispose() }
}


# Source: src/private/locking/Get-WindowsDirectoryDescriptor.ps1
function Get-WindowsDirectoryDescriptor {
    param([string]$Path)
    $identity = [Security.Principal.WindowsIdentity]::GetCurrent()
    try { $trusted = @($identity.User.Value, 'S-1-5-18', 'S-1-5-32-544', 'S-1-3-0') }
    finally { $identity.Dispose() }
    $acl = Get-Acl -LiteralPath $Path -ErrorAction Stop
    $rules = $acl.GetAccessRules($true, $true, [Security.Principal.SecurityIdentifier])
    [pscustomobject]@{
        Owner = $acl.GetOwner([Security.Principal.SecurityIdentifier]).Value
        Trusted = $trusted
        Access = @($rules | ForEach-Object { ConvertTo-WindowsAccessRule $_ })
    }
}


# Source: src/private/locking/Initialize-ControlledDirectory.ps1
function Initialize-ControlledDirectory {
    param([string]$Path)
    $safe = Assert-SafePath $Path
    if ($IsWindows) { Initialize-WindowsControlledDirectory $safe; return }
    $mode = [IO.UnixFileMode]::UserRead -bor [IO.UnixFileMode]::UserWrite -bor [IO.UnixFileMode]::UserExecute
    $null = [IO.Directory]::CreateDirectory($safe, $mode)
    Assert-UnixDirectoryOwner $safe
    $unsafe = [IO.UnixFileMode]::GroupWrite -bor [IO.UnixFileMode]::OtherWrite
    if ([IO.File]::GetUnixFileMode($safe) -band $unsafe) {
        Write-PMUError 'PMU.PermissionDenied' 'PMU data directories must not be writable by group or other users.'
    }
}


# Source: src/private/locking/Initialize-WindowsControlledDirectory.ps1
function Initialize-WindowsControlledDirectory {
    param([string]$Path)
    $null = [IO.Directory]::CreateDirectory($Path)
    Assert-WindowsDirectoryDescriptor (Get-WindowsDirectoryDescriptor $Path)
}


# Source: src/private/locking/Resolve-PMUDataRoot.ps1
function Resolve-PMUDataRoot {
    param([string]$Scope)
    if ($IsWindows) {
        $folder = 'LocalApplicationData'
        if ($Scope -eq 'AllUsers') { $folder = 'CommonApplicationData' }
        return Assert-SafePath (Join-Path ([Environment]::GetFolderPath($folder)) 'PSModuleUpdate')
    }
    if ($Scope -eq 'CurrentUser') { return Assert-SafePath (Join-Path $HOME '.local/state/PSModuleUpdate') }
    if ($IsMacOS) { return Assert-SafePath '/Library/Application Support/PSModuleUpdate' }
    Assert-SafePath '/var/lib/PSModuleUpdate'
}


# Source: src/private/locking/Resolve-RepositoryTransportRoot.ps1
function Resolve-RepositoryTransportRoot {
    $folder = [Environment]::GetFolderPath('LocalApplicationData')
    Assert-SafePath (Join-Path $folder 'PSModuleUpdate/RepositoryTransport')
}


# Source: src/private/planning/Assert-PolicyValue.ps1
function Assert-PolicyValue {
    param([string]$Name, [object]$Value, [type]$Type, [string[]]$Allowed = @())
    if ($null -eq $Value) { return }
    if ($Value -isnot $Type) { Write-PMUError 'PMU.InvalidRequest' "Policy field $Name has the wrong type." }
    if ($Allowed.Count -and $Value -cnotin $Allowed) { Write-PMUError 'PMU.InvalidRequest' "Policy field $Name has an unsupported value." }
}


# Source: src/private/planning/Assert-UpdatePolicy.ps1
function Assert-UpdatePolicy {
    param([hashtable]$Policy)
    if ($null -eq $Policy.Channel) { Write-PMUError 'PMU.InvalidRequest' 'Channel cannot be null.' }
    if ($null -eq $Policy.Scope) { Write-PMUError 'PMU.InvalidRequest' 'Scope cannot be null.' }
    Assert-PolicyValue 'Channel' $Policy.Channel ([string]) @('Stable', 'IncludePrerelease')
    Assert-PolicyValue 'Scope' $Policy.Scope ([string]) @('CurrentUser', 'AllUsers')
    Assert-PolicyValue 'ExpectedRepositoryUri' $Policy.ExpectedRepositoryUri ([uri])
    Assert-PolicyValue 'VersionRange' $Policy.VersionRange ([string])
}


# Source: src/private/planning/Complete-PlanningContext.ps1
function Complete-PlanningContext {
    param([object]$Context, [string]$RepositoryName, [pscredential]$Credential)
    $state = Get-PlanningInstallationState $Context.Module.Name $Context.Policy.Scope $Context.Reference.Guid
    Assert-ReferenceInstallationLayout $Context.Reference $state.Observed $state.RecoveryJournal
    $request = @{ Name = $Context.Module.Name; Repository = $RepositoryName; Prerelease = $Context.Policy.Channel -eq 'IncludePrerelease'; Version = $Context.Policy.VersionRange }
    $releases = @(Find-ProviderResource $request $Credential | ForEach-Object { ConvertTo-ProviderRelease $_ })
    $target = Select-UpdateTarget $releases $Context.Policy.Channel
    $Context | Add-Member -NotePropertyMembers @{ Observed = $state.Observed; Installed = $state.Installed; InstalledTree = $state.InstalledTree; Target = $target }
    $Context
}


# Source: src/private/planning/ConvertTo-UpdatePlan.ps1
function ConvertTo-UpdatePlan {
    param([object]$Context)
    $fields = [ordered]@{ PSTypeName = 'PSModuleUpdate.UpdatePlan' }
    $groups = @((Get-PlanRuntimeField $Context), (Get-PlanInstallationField $Context), (Get-PlanTargetField $Context), (Get-PlanDecisionField $Context))
    foreach ($group in $groups) {
        foreach ($entry in $group.GetEnumerator()) { $fields[$entry.Key] = $entry.Value }
    }
    [pscustomobject]$fields
}


# Source: src/private/planning/ConvertTo-UpdatePolicy.ps1
function ConvertTo-UpdatePolicy {
    param([hashtable]$Policy = @{})
    $resolved = @{ Channel = 'Stable'; Scope = 'CurrentUser'; ExpectedRepositoryUri = $null; VersionRange = $null }
    foreach ($key in $Policy.Keys) {
        if (-not $resolved.ContainsKey($key)) { Write-PMUError 'PMU.InvalidRequest' 'Unknown policy field.' }
        $resolved[$key] = $Policy[$key]
    }
    Assert-UpdatePolicy $resolved
    $resolved
}


# Source: src/private/planning/Get-ActivePlanField.ps1
function Get-ActivePlanField {
    param([object]$Context)
    if (-not $Context.Active) { return @{ Version = $null; Path = $null; Scope = $null } }
    $scope = $Context.Observed | Where-Object ManifestPath -CEQ $Context.Active.ManifestPath | Select-Object -First 1 -ExpandProperty Scope
    @{ Version = $Context.Active.Version.FullVersion; Path = $Context.Active.ManifestPath; Scope = $scope }
}


# Source: src/private/planning/Get-EligibleRelease.ps1
function Get-EligibleRelease {
    param([object[]]$Releases, [string]$Channel)
    foreach ($release in $Releases) {
        if ($Channel -eq 'Stable' -and $release.Version.IsPrerelease) { continue }
        $release
    }
}


# Source: src/private/planning/Get-LivePlanRegistry.ps1
function Get-LivePlanRegistry {
    param()
    if (-not (Get-Variable -Name PmuLivePlans -Scope Script -ErrorAction SilentlyContinue)) {
        $script:PmuLivePlans = [System.Runtime.CompilerServices.ConditionalWeakTable[object, object]]::new()
    }
    ,$script:PmuLivePlans
}


# Source: src/private/planning/Get-PlanDecisionField.ps1
function Get-PlanDecisionField {
    param([object]$Context)
    $decision = Get-UpdateDecision $Context
    [ordered]@{
        UpdateAvailable = $decision.Available
        Disposition = $decision.Disposition
        Reason = $decision.Reason
        RestartRequired = $decision.Restart
        RequiresRepositoryTrust = -not $Context.Source.Trusted
        RequiresLicenseAcceptance = $Context.Target.RequiresLicenseAcceptance
        DependencySummary = @($Context.Target.Dependencies)
    }
}


# Source: src/private/planning/Get-PlanInstallationField.ps1
function Get-PlanInstallationField {
    param([object]$Context)
    [ordered]@{
        Scope = $Context.Policy.Scope
        DestinationRoot = $Context.Installed.Root
        InstalledVersion = $Context.Installed.FullVersion
        InstalledManifestPath = $Context.Installed.ManifestPath
        ObservedInstalledVersions = @($Context.Observed)
        RepositoryName = $Context.Source.Name
        RepositoryUri = $Context.Source.Uri
        RepositoryApiVersion = $Context.Source.ApiVersion
        RepositoryTrusted = $Context.Source.Trusted
    }
}


# Source: src/private/planning/Get-PlanningContext.ps1
function Get-PlanningContext {
    param([System.Management.Automation.PSModuleInfo]$Module, [object]$Repository, [hashtable]$Policy, [pscredential]$Credential)
    $context = Initialize-PlanningContext $Module $Repository $Policy
    $lease = Open-RepositoryTransport $context.Source
    $failure = $null
    try { Complete-PlanningContext $context $lease.Name $Credential }
    catch { $failure = $_; throw }
    finally { Close-RepositoryTransport $lease $failure }
}


# Source: src/private/planning/Get-PlanningInstallationState.ps1
function Get-PlanningInstallationState {
    param([string]$Name, [string]$Scope, [guid]$Guid)
    $state = Get-RecoveryAwareInstallation $Name $Scope $Guid
    $otherScope = 'AllUsers'
    if ($Scope -eq 'AllUsers') { $otherScope = 'CurrentUser' }
    $observed = @($state.Selected; Get-InstalledModuleIdentityList $Name $otherScope)
    $installed = Select-NewestInstallation $state.Selected
    $treePath = Split-Path -Parent $installed.ManifestPath
    if ($state.FromBackup -and $installed.NumericVersion -eq $state.Journal.TargetNumericVersion) { $treePath = $state.Journal.BackupPath }
    $recovery = $null
    if ($state.FromBackup) { $recovery = $state.Journal }
    [pscustomobject]@{ Observed = $observed; Installed = $installed; InstalledTree = @(Get-FileTreeInventory $treePath); RecoveryJournal = $recovery }
}


# Source: src/private/planning/Get-PlanRuntimeField.ps1
function Get-PlanRuntimeField {
    param([object]$Context)
    $active = Get-ActivePlanField $Context
    [ordered]@{
        SchemaVersion = 1
        CreatedAtUtc = [datetime]::UtcNow
        PmuVersion = '0.1.0-preview'
        ProviderName = $Context.Provider.Name
        ProviderVersion = "$($Context.Provider.Version)"
        ModuleName = $Context.Reference.Name
        ModuleGuid = $Context.Reference.Guid
        ActiveVersion = $active.Version
        ActivePath = $active.Path
        ActiveScope = $active.Scope
    }
}


# Source: src/private/planning/Get-PlanTargetField.ps1
function Get-PlanTargetField {
    param([object]$Context)
    $target = $Context.Target.Version
    [ordered]@{
        Channel = $Context.Policy.Channel
        VersionRange = $Context.Policy.VersionRange
        TargetVersion = $target.FullVersion
        TargetNumericVersion = $target.NumericVersion
        TargetPrerelease = $target.Prerelease
        TargetIsPrerelease = $target.IsPrerelease
        TargetInstalledPath = Join-Path $Context.Installed.Root "$($Context.Reference.Name)/$($target.NumericVersion)"
    }
}


# Source: src/private/planning/Get-RecoveryAwareInstallation.ps1
function Get-RecoveryAwareInstallation {
    param([string]$Name, [string]$Scope, [guid]$Guid)
    $root = Resolve-ModuleInstallationRoot $Scope
    $identity = [pscustomobject]@{ ModuleName = $Name; ModuleGuid = $Guid; Scope = $Scope; DestinationRoot = $root; TargetNumericVersion = '0.0.0' }
    $operation = ConvertTo-InstallationOperation $identity $null @{} $null
    $journal = Read-RecoveryJournal $operation
    $selected = @(Get-RecoveryInstallationObservation $journal)
    $fromBackup = $selected.Count -gt 0
    if ($fromBackup) { $selected += @(Get-InstalledModuleIdentityList $Name $Scope $journal.TargetPath) }
    else { $selected = @(Get-InstalledModuleIdentityList $Name $Scope) }
    Assert-SelectedInstallation $selected $Guid
    [pscustomobject]@{ Selected = $selected; Journal = $journal; FromBackup = $fromBackup }
}


# Source: src/private/planning/Get-UpdateDecision.ps1
function Get-UpdateDecision {
    param([object]$Context)
    $target = $Context.Target.Version.FullVersion
    $advancesActive = Test-TargetAdvancesActiveVersion $target $Context.Active
    $installedComparison = Compare-CompleteModuleVersion $target $Context.Installed.FullVersion
    $unchanged = @{ Available = $false; Disposition = 'UpToDate'; Reason = 'No release newer than both actual active and installed versions is available.'; Restart = $false }
    if (-not $advancesActive) { return $unchanged }
    if ($installedComparison -gt 0) {
        return @{ Available = $true; Disposition = 'UpdateAvailable'; Reason = 'An exact newer release is available.'; Restart = $false }
    }
    if ($Context.Active) { return @{ Available = $false; Disposition = 'AlreadyInstalledRestartRequired'; Reason = 'A newer version is already installed in the selected scope.'; Restart = $true } }
    $unchanged
}


# Source: src/private/planning/Initialize-PlanningContext.ps1
function Initialize-PlanningContext {
    param([System.Management.Automation.PSModuleInfo]$Module, [object]$Repository, [hashtable]$Policy)
    $resolved = ConvertTo-UpdatePolicy $Policy
    [pscustomobject]@{
        Module = $Module; Policy = $resolved; Provider = Get-ProviderContext
        Reference = Get-ModuleReferenceIdentity $Module
        Active = Get-ActiveModuleIdentity $Module
        Source = Get-SelectedRepository $Repository $resolved.ExpectedRepositoryUri
    }
}


# Source: src/private/planning/Register-UpdatePlan.ps1
function Register-UpdatePlan {
    param([object]$Plan, [object]$Context, [bool]$RequiresCredential)
    $record = [pscustomobject]@{
        Snapshot = ConvertTo-Json $Plan -Depth 12 -Compress
        Module = $Context.Module
        ReferencePath = $Context.Reference.ManifestPath
        WasLoaded = @(Get-Module -Name $Context.Module.Name | Where-Object { [object]::ReferenceEquals($_, $Context.Module) }).Count -gt 0
        RequiresCredential = $RequiresCredential
        SourceKind = if ($Plan.RepositoryName) { 'Registered' } else { 'TemporaryUri' }
        InstalledTree = @($Context.InstalledTree)
    }
    (Get-LivePlanRegistry).Add($Plan, $record)
    $Plan
}


# Source: src/private/planning/Select-NewestInstallation.ps1
function Select-NewestInstallation {
    param([object[]]$Installations)
    $newest = $null
    foreach ($installation in $Installations) {
        if (-not $newest) { $newest = $installation; continue }
        if ((Compare-CompleteModuleVersion $installation.FullVersion $newest.FullVersion) -gt 0) { $newest = $installation }
    }
    $newest
}


# Source: src/private/planning/Select-UpdateTarget.ps1
function Select-UpdateTarget {
    param([object[]]$Releases, [string]$Channel)
    $target = $null
    foreach ($release in (Get-EligibleRelease $Releases $Channel)) {
        if (-not $target) { $target = $release; continue }
        if ((Compare-CompleteModuleVersion $release.Version.FullVersion $target.Version.FullVersion) -gt 0) { $target = $release }
    }
    if (-not $target) { Write-PMUError 'PMU.PackageNotFound' 'No eligible package was found in the explicit repository.' }
    $target
}


# Source: src/private/planning/Test-TargetAdvancesActiveVersion.ps1
function Test-TargetAdvancesActiveVersion {
    param([string]$TargetVersion, [object]$Active)
    if (-not $Active) { return $true }
    (Compare-CompleteModuleVersion $TargetVersion $Active.Version.FullVersion) -gt 0
}


# Source: src/private/planning/Test-UpdatePlan.ps1
function Test-UpdatePlan {
    param([object]$Plan)
    $record = $null
    if (-not (Get-LivePlanRegistry).TryGetValue($Plan, [ref]$record)) {
        Write-PMUError 'PMU.InvalidPlan' 'A live plan returned by Get-PSModuleUpdate in this module instance is required.'
    }
    if ($Plan.PSObject.TypeNames[0] -cne 'PSModuleUpdate.UpdatePlan' -or $record.Snapshot -cne (ConvertTo-Json $Plan -Depth 12 -Compress)) {
        Write-PMUError 'PMU.InvalidPlan' 'The plan was modified or is not a live schema-1 plan.'
    }
    $record
}


# Source: src/private/provider/ConvertTo-ProviderRelease.ps1
function ConvertTo-ProviderRelease {
    param([object]$Resource)
    $version = "$($Resource.Version)"
    if ($Resource.Prerelease) { $version += "-$($Resource.Prerelease)" }
    $identity = ConvertTo-CompleteModuleVersion $version
    $license = Get-ProviderLicenseRequirement $Resource
    [pscustomobject]@{ Name = $Resource.Name; Version = $identity; RequiresLicenseAcceptance = $license; Dependencies = @($Resource.Dependencies | ForEach-Object { "$($_.Name)" }) }
}


# Source: src/private/provider/Find-ProviderResource.ps1
function Find-ProviderResource {
    param([hashtable]$Request, [pscredential]$Credential)
    $parameters = @{ Name = $Request.Name; Repository = $Request.Repository; Prerelease = $Request.Prerelease; ErrorAction = 'Stop'; WarningAction = 'SilentlyContinue'; Verbose = $false; Debug = $false }
    $parameters.Version = '*'
    if ($Request.Version) { $parameters.Version = $Request.Version }
    if ($Credential) { $parameters.Credential = $Credential }
    try { Microsoft.PowerShell.PSResourceGet\Find-PSResource @parameters }
    catch { Write-ProviderError $_ 'PMU.DiscoveryFailed' }
}


# Source: src/private/provider/Get-ProviderContext.ps1
function Get-ProviderContext {
    param()
    $provider = Get-Module Microsoft.PowerShell.PSResourceGet
    if (-not $provider) {
        try { $provider = Import-Module Microsoft.PowerShell.PSResourceGet -RequiredVersion 1.2.0 -PassThru -ErrorAction Stop }
        catch { Write-PMUError 'PMU.ProviderUnavailable' 'PSResourceGet 1.2.0 must be available; PMU does not install it.' }
    }
    if ($provider.Version -ne [version]'1.2.0') {
        Write-PMUError 'PMU.ProviderIncompatible' 'Only the qualified PSResourceGet 1.2.0 baseline is enabled.'
    }
    $provider
}


# Source: src/private/provider/Get-ProviderLicenseRequirement.ps1
function Get-ProviderLicenseRequirement {
    param([object]$Resource)
    $metadata = $Resource.AdditionalMetadata
    if (-not $metadata) { return $true }
    $keys = @($metadata.Keys | Where-Object { $_ -ieq 'RequireLicenseAcceptance' })
    if ($keys.Count -ne 1) { return $true }
    $value = $metadata[$keys[0]]
    if ($value -is [bool]) { return $value }
    if ($value -isnot [string]) { return $true }
    $value -ine 'false'
}


# Source: src/private/provider/Get-ProviderMutationParameter.ps1
function Get-ProviderMutationParameter {
    param([object]$Operation)
    $plan = $Operation.Plan
    $parameters = @{
        Name = $plan.ModuleName; Version = "[$($plan.TargetVersion)]"; Repository = Get-OperationRepositoryName $Operation
        Prerelease = $plan.TargetIsPrerelease; TrustRepository = [bool]$Operation.Consent.TrustRepository
        AcceptLicense = [bool]$Operation.Consent.AcceptLicense; Quiet = $true; Confirm = $false
        ErrorAction = 'Stop'; WarningAction = 'SilentlyContinue'; Verbose = $false; Debug = $false
    }
    if ($Operation.Credential) { $parameters.Credential = $Operation.Credential }
    $parameters
}


# Source: src/private/provider/Install-ProviderResource.ps1
function Install-ProviderResource {
    param([object]$Operation)
    $parameters = Get-ProviderMutationParameter $Operation
    $parameters.Scope = $Operation.Plan.Scope
    if ($Operation.Reinstall) { $parameters.Reinstall = $true }
    try { $null = Microsoft.PowerShell.PSResourceGet\Install-PSResource @parameters }
    catch { Write-ProviderError $_ 'PMU.InstallationFailed' }
}


# Source: src/private/provider/Save-ProviderResource.ps1
function Save-ProviderResource {
    param([object]$Operation)
    $parameters = Get-ProviderMutationParameter $Operation
    $parameters.Path = $Operation.StageRoot
    $parameters.IncludeXml = $true
    try { $null = Microsoft.PowerShell.PSResourceGet\Save-PSResource @parameters }
    catch { Write-ProviderError $_ 'PMU.InstallationFailed' }
}


# Source: src/private/provider/Write-ProviderError.ps1
function Write-ProviderError {
    param([System.Management.Automation.ErrorRecord]$Failure, [string]$DefaultId)
    $id = $DefaultId
    if ($Failure.Exception -is [UnauthorizedAccessException] -or $Failure.Exception.Message -match '(?i)access.+denied|permission denied') { $id = 'PMU.PermissionDenied' }
    if ($Failure.Exception.Message -match '(?i)\b401\b|\b403\b|unauthorized|authentication') { $id = 'PMU.AuthenticationFailed' }
    Write-PMUError $id 'The provider operation failed. Provider diagnostics were withheld to protect credentials.' 'Check repository authentication and scope permissions, then obtain a fresh plan. PMU never elevates or changes scope.'
}


# Source: src/private/recovery/Assert-BackupReferenceIdentity.ps1
function Assert-BackupReferenceIdentity {
    param([object]$Reference, [object]$Journal)
    if (-not $Journal) { Write-PMUError 'PMU.UnsupportedInstallation' 'The reference must identify an installed module or a validated unfinished-operation backup.' }
    $backup = Get-VerifiedRecoveryBackup $Journal 'PMU.RecoveryRequired'
    Assert-MatchingProperty $Reference $backup @('Name', 'Guid', 'ManifestPath') 'PMU.UnsupportedInstallation'
    if ($Reference.Version.FullVersion -cne $backup.Version.FullVersion) {
        Write-PMUError 'PMU.ModuleIdentityMismatch' 'The backup reference full version differs from the verified previous installation.'
    }
}


# Source: src/private/recovery/Assert-RecoveryJournal.ps1
function Assert-RecoveryJournal {
    param([object]$Journal, [object]$Operation)
    Assert-RecoveryJournalSchema $Journal
    $plan = $Operation.Plan
    $version = ConvertTo-CompleteModuleVersion $Journal.TargetVersion
    $expected = [pscustomobject]@{ SchemaVersion = 1; ModuleName = $plan.ModuleName; ModuleGuid = "$($plan.ModuleGuid)"; Scope = $plan.Scope; DestinationRoot = $plan.DestinationRoot; TargetNumericVersion = $version.NumericVersion }
    Assert-MatchingProperty $Journal $expected @('SchemaVersion', 'ModuleName', 'ModuleGuid', 'Scope', 'DestinationRoot', 'TargetNumericVersion') 'PMU.RecoveryRequired'
    Assert-RecoveryPath $Journal $Operation
    if ($Journal.State -notin @('Preparation', 'BackupReady', 'ReplacementInProgress', 'Verification', 'Completed', 'RecoveryRequired', 'RecoveryCompleted')) {
        Write-PMUError 'PMU.RecoveryRequired' 'Recovery journal state is invalid.'
    }
}


# Source: src/private/recovery/Assert-RecoveryJournalSchema.ps1
function Assert-RecoveryJournalSchema {
    param([object]$Journal)
    $fields = 'SchemaVersion ModuleName Scope DestinationRoot ModuleGuid PreviousVersion TargetVersion TargetNumericVersion TargetPath JournalPath StageRoot StagedPath BackupPath Reinstall State RecoveryStatus StagedTree BackupTree'.Split(' ')
    $actual = @($Journal.PSObject.Properties.Name | Sort-Object)
    if (($actual -join '|') -cne (($fields | Sort-Object) -join '|')) {
        Write-PMUError 'PMU.RecoveryRequired' 'The recovery journal schema is invalid.'
    }
    if ($Journal.Reinstall -isnot [bool]) { Write-PMUError 'PMU.RecoveryRequired' 'The recovery journal mutation flag is not Boolean.' }
    if ($Journal.StagedTree -isnot [array]) { Write-PMUError 'PMU.RecoveryRequired' 'The recovery reference tree is not an array.' }
    if ($Journal.BackupTree -isnot [array]) { Write-PMUError 'PMU.RecoveryRequired' 'The recovery backup tree is not an array.' }
}


# Source: src/private/recovery/Assert-RecoveryPath.ps1
function Assert-RecoveryPath {
    param([object]$Journal, [object]$Operation)
    $expected = @{
        JournalPath = $Operation.JournalPath; StageRoot = $Operation.StageRoot; BackupPath = $Operation.BackupPath
        StagedPath = Join-Path $Operation.StageRoot "$($Journal.ModuleName)/$($Journal.TargetNumericVersion)"
        TargetPath = Join-Path $Operation.Plan.DestinationRoot "$($Journal.ModuleName)/$($Journal.TargetNumericVersion)"
    }
    foreach ($key in $expected.Keys) {
        if ($Journal.$key -cne $expected[$key]) { Write-PMUError 'PMU.RecoveryRequired' 'Recovery paths are not the expected PMU-controlled paths.' }
        $null = Assert-SafePath $Journal.$key
    }
}


# Source: src/private/recovery/Assert-RecoveryTargetIdentity.ps1
function Assert-RecoveryTargetIdentity {
    param([object]$Journal)
    $manifest = Join-Path $Journal.TargetPath "$($Journal.ModuleName).psd1"
    $data = Get-SurvivingManifestData $manifest
    $expected = @{ GUID = $Journal.ModuleGuid; ModuleVersion = $Journal.TargetNumericVersion }
    $errorId = @{ GUID = 'PMU.GuidMismatch'; ModuleVersion = 'PMU.ModuleIdentityMismatch' }
    foreach ($key in $expected.Keys) {
        if (-not $data[$key]) { continue }
        if ("$($data[$key])" -ine $expected[$key]) {
            Write-PMUError $errorId[$key] 'Recovery refuses to delete an installation with an unexpected manifest identity.'
        }
    }
    Assert-RecoveryTargetVersion $data $Journal
}


# Source: src/private/recovery/Assert-RecoveryTargetVersion.ps1
function Assert-RecoveryTargetVersion {
    param([hashtable]$Manifest, [object]$Journal)
    if (-not $Manifest['ModuleVersion']) { return }
    $version = Get-ManifestModuleVersion $Manifest
    if ($version.FullVersion -cnotin @($Journal.PreviousVersion, $Journal.TargetVersion)) {
        Write-PMUError 'PMU.ModuleIdentityMismatch' 'Recovery refuses to remove an unrelated full-version identity from the numeric target.'
    }
}


# Source: src/private/recovery/Clear-CompletedOperation.ps1
function Clear-CompletedOperation {
    param([object]$Operation)
    $journal = Read-RecoveryJournal $Operation
    if (-not $journal) { return }
    if ($journal.State -notin @('Completed', 'RecoveryCompleted')) {
        Write-PMUError 'PMU.RecoveryRequired' 'Unfinished recovery evidence cannot be discarded.'
    }
    foreach ($path in @($Operation.StageRoot, $Operation.BackupPath)) {
        $null = Assert-SafePath $path
        if (Test-Path -LiteralPath $path) {
            $null = @(Get-FileTreeInventory $path)
            Remove-Item -LiteralPath $path -Recurse -Force -ErrorAction Stop
        }
    }
}


# Source: src/private/recovery/Clear-ControlledTarget.ps1
function Clear-ControlledTarget {
    param([object]$Journal)
    $expected = Join-Path $Journal.DestinationRoot "$($Journal.ModuleName)/$($Journal.TargetNumericVersion)"
    if ($expected -cne $Journal.TargetPath) { Write-PMUError 'PMU.RecoveryRequired' 'Refusing an unexpected removal path.' }
    $null = Assert-SafePath $expected
    if (Test-Path -LiteralPath $expected) {
        $null = @(Get-FileTreeInventory $expected)
        Assert-RecoveryTargetIdentity $Journal
        Remove-Item -LiteralPath $expected -Recurse -Force -ErrorAction Stop
    }
}


# Source: src/private/recovery/Complete-RecoveryTarget.ps1
function Complete-RecoveryTarget {
    param([object]$Journal, [object]$Operation)
    if (-not (Test-RecoveryTargetComplete $Journal)) { return $false }
    $Journal.RecoveryStatus = 'TargetVerified'
    Write-RecoveryJournalState $Journal 'Completed'
    $Operation.RecoveryStatus = 'TargetVerified'
    $true
}


# Source: src/private/recovery/ConvertTo-RecoveryJournal.ps1
function ConvertTo-RecoveryJournal {
    param([object]$Operation, [object[]]$StagedTree)
    $plan = $Operation.Plan
    [pscustomobject]@{
        SchemaVersion = 1; ModuleName = $plan.ModuleName; Scope = $plan.Scope; DestinationRoot = $plan.DestinationRoot
        ModuleGuid = "$($plan.ModuleGuid)"; PreviousVersion = $plan.InstalledVersion; TargetVersion = $plan.TargetVersion
        TargetNumericVersion = $plan.TargetNumericVersion; TargetPath = $plan.TargetInstalledPath
        JournalPath = $Operation.JournalPath; StageRoot = $Operation.StageRoot; StagedPath = $Operation.StagedPath
        BackupPath = $Operation.BackupPath; Reinstall = $Operation.Reinstall
        State = 'Preparation'; RecoveryStatus = 'NotRequired'; StagedTree = @($StagedTree); BackupTree = @()
    }
}


# Source: src/private/recovery/Copy-InstallationBackup.ps1
function Copy-InstallationBackup {
    param([object]$Operation)
    $journal = $Operation.Journal
    $null = Assert-SafePath $Operation.Plan.TargetInstalledPath
    $journal.BackupTree = @(Get-FileTreeInventory $Operation.Plan.TargetInstalledPath)
    Copy-Item -LiteralPath $Operation.Plan.TargetInstalledPath -Destination $Operation.BackupPath -Recurse -ErrorAction Stop
    Assert-FileTreeEqual $journal.BackupTree @(Get-FileTreeInventory $Operation.BackupPath)
    Write-RecoveryJournalState $journal 'BackupReady'
}


# Source: src/private/recovery/Get-RecoveryInstallationObservation.ps1
function Get-RecoveryInstallationObservation {
    param([object]$Journal)
    if (-not $Journal) { return }
    if ($Journal.State -in @('Completed', 'RecoveryCompleted', 'Preparation')) { return }
    if (-not $Journal.Reinstall) { return }
    $backup = Get-VerifiedRecoveryBackup $Journal 'PMU.RecoveryRequired'
    [pscustomobject]@{
        FullVersion = $backup.Version.FullVersion; NumericVersion = $backup.Version.NumericVersion; Prerelease = $backup.Version.Prerelease
        Scope = $Journal.Scope; Root = $Journal.DestinationRoot
        ManifestPath = Join-Path $Journal.TargetPath "$($Journal.ModuleName).psd1"; Guid = $backup.Guid
    }
}


# Source: src/private/recovery/Get-SurvivingManifestData.ps1
function Get-SurvivingManifestData {
    param([string]$Path)
    if (-not (Test-Path -LiteralPath $Path)) { return @{} }
    try { Import-PowerShellDataFile -LiteralPath $Path -ErrorAction Stop }
    catch { @{} } # A provider interruption can leave a truncated manifest in a journal-owned path.
}


# Source: src/private/recovery/Get-VerifiedRecoveryBackup.ps1
function Get-VerifiedRecoveryBackup {
    param([object]$Journal, [string]$ErrorId = 'PMU.RecoveryFailed')
    $manifest = Join-Path $Journal.BackupPath "$($Journal.ModuleName).psd1"
    $identity = Get-ManifestIdentity $manifest
    if ($identity.Guid -ne [guid]$Journal.ModuleGuid -or $identity.Version.FullVersion -cne $Journal.PreviousVersion) {
        Write-PMUError $ErrorId 'The surviving backup does not match the previous identity.'
    }
    Assert-FileTreeEqual @($Journal.BackupTree) @(Get-FileTreeInventory $Journal.BackupPath) $ErrorId
    $identity
}


# Source: src/private/recovery/Invoke-RecoveryRestoration.ps1
function Invoke-RecoveryRestoration {
    param([object]$Journal, [object]$Operation)
    try {
        if ($Journal.State -in @('Preparation', 'BackupReady')) {
            Write-RecoveryJournalState $Journal 'RecoveryCompleted'
        } else { Restore-InstallationBackup $Journal $Operation }
    } catch { Write-PMUError 'PMU.RecoveryFailed' 'Recovery could not be verified. The journal and surviving backup were preserved.' }
}


# Source: src/private/recovery/Read-RecoveryJournal.ps1
function Read-RecoveryJournal {
    param([object]$Operation)
    if (-not (Test-Path -LiteralPath $Operation.JournalPath)) { return $null }
    try {
        $null = Assert-SafePath $Operation.JournalPath
        $journal = Get-Content -LiteralPath $Operation.JournalPath -Raw -ErrorAction Stop | ConvertFrom-Json -ErrorAction Stop
        Assert-RecoveryJournal $journal $Operation
        return $journal
    }
    catch { Write-PMUError 'PMU.RecoveryRequired' 'The journal or its paths are unsafe. Preserve PMU data for manual inspection.' }
}


# Source: src/private/recovery/Repair-UnfinishedInstallation.ps1
function Repair-UnfinishedInstallation {
    param([object]$Operation)
    $journal = Read-RecoveryJournal $Operation
    if (-not $journal -or $journal.State -in @('Completed', 'RecoveryCompleted')) { return }
    Write-InstallationPhase $Operation.Plan 'Recovering'
    if (Complete-RecoveryTarget $journal $Operation) { return }
    Invoke-RecoveryRestoration $journal $Operation
    $null = (Get-LivePlanRegistry).Remove($Operation.Plan)
    Write-PMUError 'PMU.PlanStale' 'An unfinished operation was recovered. Obtain a fresh plan before another update.'
}


# Source: src/private/recovery/Restore-InstallationBackup.ps1
function Restore-InstallationBackup {
    param([object]$Journal, [object]$Operation)
    Assert-RecoveryJournal $Journal $Operation
    if ($Journal.Reinstall) { $null = Get-VerifiedRecoveryBackup $Journal }
    Clear-ControlledTarget $Journal
    if ($Journal.Reinstall) {
        Copy-Item -LiteralPath $Journal.BackupPath -Destination $Journal.TargetPath -Recurse -ErrorAction Stop
        Assert-FileTreeEqual @($Journal.BackupTree) @(Get-FileTreeInventory $Journal.TargetPath) 'PMU.RecoveryFailed'
    }
    $Journal.RecoveryStatus = 'Restored'
    Write-RecoveryJournalState $Journal 'RecoveryCompleted'
}


# Source: src/private/recovery/Test-RecoveryTargetComplete.ps1
function Test-RecoveryTargetComplete {
    param([object]$Journal)
    try {
        $identity = Get-ManifestIdentity (Join-Path $Journal.TargetPath "$($Journal.ModuleName).psd1")
        if ($identity.Guid -ne [guid]$Journal.ModuleGuid -or $identity.Version.FullVersion -cne $Journal.TargetVersion) { return $false }
        Assert-FileTreeEqual @($Journal.StagedTree) @(Get-FileTreeInventory $Journal.TargetPath)
        return $true
    }
    catch { return $false }
}


# Source: src/private/recovery/Write-DurableJournal.ps1
function Write-DurableJournal {
    param([object]$Journal)
    $path = Assert-SafePath $Journal.JournalPath
    $temporary = "$path.new"
    $null = Assert-SafePath $temporary
    $bytes = [Text.Encoding]::UTF8.GetBytes((ConvertTo-Json $Journal -Depth 12 -Compress))
    $stream = [IO.File]::Open($temporary, [IO.FileMode]::Create, [IO.FileAccess]::Write, [IO.FileShare]::None)
    try { $stream.Write($bytes); $stream.Flush($true) }
    finally { $stream.Dispose() }
    [IO.File]::Move($temporary, $path, $true)
}


# Source: src/private/recovery/Write-RecoveryJournalState.ps1
function Write-RecoveryJournalState {
    param([object]$Journal, [string]$State)
    $transitions = @{
        Preparation = @('Preparation', 'BackupReady', 'ReplacementInProgress', 'RecoveryCompleted')
        BackupReady = @('BackupReady', 'ReplacementInProgress', 'RecoveryCompleted')
        ReplacementInProgress = @('ReplacementInProgress', 'Verification', 'RecoveryRequired', 'RecoveryCompleted', 'Completed')
        Verification = @('Verification', 'Completed', 'RecoveryRequired', 'RecoveryCompleted')
        RecoveryRequired = @('RecoveryRequired', 'RecoveryCompleted', 'Completed')
        Completed = @('Completed'); RecoveryCompleted = @('RecoveryCompleted')
    }
    if ($State -notin $transitions[$Journal.State]) { Write-PMUError 'PMU.RecoveryRequired' 'Invalid recovery journal state transition.' }
    $Journal.State = $State
    Write-DurableJournal $Journal
}


# Source: src/private/repository/Assert-OwnedRepositoryConfiguration.ps1
function Assert-OwnedRepositoryConfiguration {
    param([object]$Registration, [object]$Receipt)
    $actual = [pscustomobject]@{
        RegistrationName = $Registration.Name; UriHash = Get-RepositoryUriHash ([uri]$Registration.Uri)
        ApiVersion = "$($Registration.ApiVersion)"; Trusted = [bool]$Registration.Trusted; Priority = $Registration.Priority
    }
    Assert-MatchingProperty $actual $Receipt @('RegistrationName', 'UriHash', 'ApiVersion', 'Trusted', 'Priority') 'PMU.RepositoryRecoveryRequired'
}


# Source: src/private/repository/Assert-RepositoryBinding.ps1
function Assert-RepositoryBinding {
    param([object]$Plan, [string]$TransportName)
    $name = $Plan.RepositoryName
    if ($TransportName) { $name = $TransportName }
    if (-not $name) { $null = Assert-RepositoryUri ([uri]$Plan.RepositoryUri); return }
    $source = Get-RepositorySnapshot $name ([uri]$Plan.RepositoryUri)
    $expected = [pscustomobject]@{
        Name = $name; Uri = $Plan.RepositoryUri
        ApiVersion = $Plan.RepositoryApiVersion; Trusted = $Plan.RepositoryTrusted
    }
    Assert-MatchingProperty $source $expected @('Name', 'Uri', 'ApiVersion', 'Trusted') 'PMU.RepositoryChanged'
}


# Source: src/private/repository/Assert-RepositoryName.ps1
function Assert-RepositoryName {
    param([string]$Name)
    if ([string]::IsNullOrWhiteSpace($Name) -or [System.Management.Automation.WildcardPattern]::ContainsWildcardCharacters($Name)) {
        Write-PMUError 'PMU.InvalidRequest' 'Repository must be one literal registered name.'
    }
}


# Source: src/private/repository/Assert-RepositoryReceipt.ps1
function Assert-RepositoryReceipt {
    param([object]$Receipt)
    $expected = @('ApiVersion', 'OperationId', 'OwnerPid', 'OwnerStartedUtcTicks', 'Priority', 'RegistrationName', 'SchemaVersion', 'Trusted', 'UriHash')
    if ((@($Receipt.PSObject.Properties.Name | Sort-Object) -join ',') -cne ($expected -join ',')) {
        Write-PMUError 'PMU.RepositoryRecoveryRequired' 'Repository receipt schema is invalid.'
    }
    Assert-RepositoryReceiptIdentity $Receipt
    Assert-MatchingProperty $Receipt ([pscustomobject]@{ Trusted = $false; Priority = 50 }) @('Trusted', 'Priority') 'PMU.RepositoryRecoveryRequired'
}


# Source: src/private/repository/Assert-RepositoryReceiptIdentity.ps1
function Assert-RepositoryReceiptIdentity {
    param([object]$Receipt)
    $values = @(
        "$($Receipt.SchemaVersion)" -ceq '1'
        "$($Receipt.OperationId)" -cmatch '^[a-f0-9]{32}$'
        "$($Receipt.RegistrationName)" -ceq "pmu-uri-$($Receipt.OperationId)"
        "$($Receipt.OwnerPid)" -match '^[1-9][0-9]{0,9}$'
        "$($Receipt.OwnerStartedUtcTicks)" -match '^[0-9]{18}$'
        "$($Receipt.UriHash)" -cmatch '^[A-F0-9]{64}$'
        "$($Receipt.ApiVersion)" -cin @('Unknown', 'V2', 'V3', 'Local', 'NugetServer', 'ContainerRegistry')
    )
    if ($false -in $values) { Write-PMUError 'PMU.RepositoryRecoveryRequired' 'Repository receipt identity is invalid.' }
}


# Source: src/private/repository/Assert-RepositoryUri.ps1
function Assert-RepositoryUri {
    param([uri]$Uri)
    if (-not $Uri.IsAbsoluteUri) { Write-PMUError 'PMU.InvalidRequest' 'Repository endpoints must be absolute.' }
    if ("$($Uri.UserInfo)$($Uri.Query)$($Uri.Fragment)") {
        Write-PMUError 'PMU.InvalidRequest' 'Repository endpoints must be absolute and must not contain credentials, queries, or fragments.'
    }
    $Uri.AbsoluteUri.TrimEnd('/')
}


# Source: src/private/repository/Clear-OwnedRepositoryReceipt.ps1
function Clear-OwnedRepositoryReceipt {
    param([object]$Entry)
    $registered = @(Get-RepositoryRegistration $Entry.Receipt.RegistrationName)
    if ($registered.Count -gt 1) { Write-PMUError 'PMU.RepositoryRecoveryRequired' 'Owned registration is ambiguous.' }
    if ($registered.Count) {
        Assert-OwnedRepositoryConfiguration $registered[0] $Entry.Receipt
        try { Microsoft.PowerShell.PSResourceGet\Unregister-PSResourceRepository -Name $Entry.Receipt.RegistrationName -Confirm:$false -ErrorAction Stop }
        catch { Write-ProviderError $_ 'PMU.RepositoryCleanupFailed' }
    }
    Remove-Item -LiteralPath (Assert-SafePath $Entry.Path) -ErrorAction Stop
}


# Source: src/private/repository/Close-OwnedRepositoryTransport.ps1
function Close-OwnedRepositoryTransport {
    param([object]$Lease)
    $root = Resolve-RepositoryTransportRoot
    $lock = Enter-RepositoryRegistryLock $root
    try {
        $receipt = Read-RepositoryReceipt $Lease.ReceiptPath $root
        Assert-MatchingProperty $receipt $Lease.Receipt @('OperationId', 'RegistrationName', 'OwnerPid', 'OwnerStartedUtcTicks', 'UriHash') 'PMU.RepositoryRecoveryRequired'
        if ($receipt.OwnerPid -ne $PID) { Write-PMUError 'PMU.RepositoryRecoveryRequired' 'A different operation owns this registration.' }
        Clear-OwnedRepositoryReceipt ([pscustomobject]@{ Path = $Lease.ReceiptPath; Receipt = $receipt })
    }
    finally { $lock.Dispose() }
}


# Source: src/private/repository/Close-RepositoryTransport.ps1
function Close-RepositoryTransport {
    param([object]$Lease, [System.Management.Automation.ErrorRecord]$Original)
    if (-not $Lease.ReceiptPath) { return }
    try { Close-OwnedRepositoryTransport $Lease }
    catch { Write-RepositoryCleanupFailure $Original }
}


# Source: src/private/repository/Complete-OwnedRepositoryBinding.ps1
function Complete-OwnedRepositoryBinding {
    param([object]$Lease, [object]$Registration)
    if ($Lease.Receipt.ApiVersion -ne 'Unknown') { Assert-OwnedRepositoryConfiguration $Registration $Lease.Receipt; return }
    $bound = $Lease.Receipt.PSObject.Copy()
    $bound.ApiVersion = "$($Registration.ApiVersion)"
    if ($bound.ApiVersion -eq 'Unknown') { Write-PMUError 'PMU.RepositoryUnavailable' 'The provider did not resolve its repository API.' }
    Assert-RepositoryReceipt $bound
    Assert-OwnedRepositoryConfiguration $Registration $bound
    $Lease.Receipt = $bound
    Write-BoundRepositoryReceipt $Lease
    $Lease.Source.ApiVersion = $bound.ApiVersion
}


# Source: src/private/repository/ConvertTo-RepositoryReceipt.ps1
function ConvertTo-RepositoryReceipt {
    param([object]$Source)
    $operationId = [guid]::NewGuid().ToString('N')
    [pscustomobject]@{
        SchemaVersion = 1; OperationId = $operationId; RegistrationName = "pmu-uri-$operationId"
        OwnerPid = $PID; OwnerStartedUtcTicks = Get-RepositoryProcessStart $PID
        UriHash = Get-RepositoryUriHash ([uri]$Source.Uri)
        ApiVersion = "$($Source.ApiVersion)"; Trusted = $false; Priority = 50
    }
}


# Source: src/private/repository/Get-OperationRepositoryName.ps1
function Get-OperationRepositoryName {
    param([object]$Operation)
    if ($Operation.RepositoryName) { return $Operation.RepositoryName }
    $Operation.Plan.RepositoryName
}


# Source: src/private/repository/Get-OwnedRepositoryName.ps1
function Get-OwnedRepositoryName {
    $root = Resolve-RepositoryTransportRoot
    foreach ($entry in Get-RepositoryReceiptEntry $root) {
        $registered = @(Get-RepositoryRegistration $entry.Receipt.RegistrationName)
        if (-not $registered.Count) { continue }
        Assert-OwnedRepositoryConfiguration $registered[0] $entry.Receipt
        $entry.Receipt.RegistrationName
    }
}


# Source: src/private/repository/Get-RepositoryReceiptEntry.ps1
function Get-RepositoryReceiptEntry {
    param([string]$Root)
    $null = Assert-SafePath $Root
    if (-not (Test-Path -LiteralPath $Root)) { return }
    Initialize-ControlledDirectory $Root
    foreach ($file in Get-ChildItem -LiteralPath $Root -Filter '*.json' -File) {
        [pscustomobject]@{ Path = $file.FullName; Receipt = Read-RepositoryReceipt $file.FullName $Root }
    }
}


# Source: src/private/repository/Get-RepositoryRegistration.ps1
function Get-RepositoryRegistration {
    param([string]$Name)
    try { @(Microsoft.PowerShell.PSResourceGet\Get-PSResourceRepository -ErrorAction Stop | Where-Object Name -CEQ $Name) }
    catch { Write-ProviderError $_ 'PMU.RepositoryUnavailable' }
}


# Source: src/private/repository/Get-RepositorySnapshot.ps1
function Get-RepositorySnapshot {
    param([string]$Name, [uri]$ExpectedUri)
    Assert-RepositoryName $Name
    try { $repositories = @(Microsoft.PowerShell.PSResourceGet\Get-PSResourceRepository -Name $Name -ErrorAction Stop) }
    catch { Write-ProviderError $_ 'PMU.RepositoryUnavailable' }
    if ($repositories.Count -ne 1) { Write-PMUError 'PMU.RepositoryUnavailable' 'Exactly one registered repository is required.' }
    $repository = $repositories[0]
    $uri = Assert-RepositoryUri $repository.Uri
    if ($ExpectedUri -and $uri -ne (Assert-RepositoryUri $ExpectedUri)) { Write-PMUError 'PMU.RepositoryChanged' 'Repository endpoint does not match policy.' }
    [pscustomobject]@{ Name = $repository.Name; Uri = $uri; ApiVersion = "$($repository.ApiVersion)"; Trusted = [bool]$repository.Trusted }
}


# Source: src/private/repository/Get-RepositorySnapshotByUri.ps1
function Get-RepositorySnapshotByUri {
    param([uri]$RepositoryUri, [uri]$ExpectedUri)
    $uri = Assert-RepositoryUri $RepositoryUri
    if ($ExpectedUri -and $uri -cne (Assert-RepositoryUri $ExpectedUri)) { Write-PMUError 'PMU.RepositoryChanged' 'Repository endpoint does not match policy.' }
    try { $repositories = @(Microsoft.PowerShell.PSResourceGet\Get-PSResourceRepository -ErrorAction Stop) }
    catch { Write-ProviderError $_ 'PMU.RepositoryUnavailable' }
    $owned = @(Get-OwnedRepositoryName)
    $selected = @($repositories | Where-Object { $_.Name -cnotin $owned -and ([uri]$_.Uri).AbsoluteUri.TrimEnd('/') -ceq $uri })
    if ($selected.Count -gt 1) { Write-PMUError 'PMU.RepositoryUnavailable' 'RepositoryUri matches multiple consumer registrations.' }
    if (-not $selected.Count) { return [pscustomobject]@{ Name = $null; Uri = $uri; ApiVersion = 'Unknown'; Trusted = $false } }
    Get-RepositorySnapshot $selected[0].Name ([uri]$uri)
}


# Source: src/private/repository/Get-RepositoryUriHash.ps1
function Get-RepositoryUriHash {
    param([uri]$Uri)
    $normalized = Assert-RepositoryUri $Uri
    [Convert]::ToHexString([Security.Cryptography.SHA256]::HashData([Text.Encoding]::UTF8.GetBytes($normalized)))
}


# Source: src/private/repository/Get-SelectedRepository.ps1
function Get-SelectedRepository {
    param([object]$Selection, [uri]$ExpectedUri)
    if ($Selection -is [string]) { return Get-RepositorySnapshot $Selection $ExpectedUri }
    if ($Selection.Uri) { return Get-RepositorySnapshotByUri $Selection.Uri $ExpectedUri }
    Get-RepositorySnapshot $Selection.Name $ExpectedUri
}


# Source: src/private/repository/Invoke-OwnedRepositoryRegistration.ps1
function Invoke-OwnedRepositoryRegistration {
    param([object]$Source, [string]$Root)
    $receipt = ConvertTo-RepositoryReceipt $Source
    $path = Write-RepositoryReceipt $receipt $Root
    $lease = [pscustomobject]@{ Name = $receipt.RegistrationName; ReceiptPath = $path; Root = $Root; Receipt = $receipt; Source = $Source }
    try { Register-OwnedRepository $lease }
    catch {
        $failure = $_
        if (-not (Test-Path -LiteralPath $path -PathType Leaf)) { throw $failure }
        try { Clear-OwnedRepositoryReceipt ([pscustomobject]@{ Path = $path; Receipt = $lease.Receipt }) }
        catch { Write-RepositoryCleanupFailure $failure }
        throw $failure
    }
    $lease
}


# Source: src/private/repository/Open-RepositoryTransport.ps1
function Open-RepositoryTransport {
    param([object]$Source, [int]$TimeoutMilliseconds = 5000)
    $null = Assert-RepositoryUri ([uri]$Source.Uri)
    if ($Source.Name) { return [pscustomobject]@{ Name = $Source.Name; ReceiptPath = $null } }
    $root = Resolve-RepositoryTransportRoot
    $lock = Enter-RepositoryRegistryLock $root $TimeoutMilliseconds
    try {
        Repair-RepositoryReceipt $root
        Invoke-OwnedRepositoryRegistration $Source $root
    }
    finally { $lock.Dispose() }
}


# Source: src/private/repository/Read-RepositoryReceipt.ps1
function Read-RepositoryReceipt {
    param([string]$Path, [string]$Root)
    $safe = Assert-SafePath $Path
    if ([IO.Path]::GetDirectoryName($safe) -cne (Assert-SafePath $Root)) { Write-PMUError 'PMU.RepositoryRecoveryRequired' 'Repository receipt path is invalid.' }
    if ((Get-Item -LiteralPath $safe).Length -gt 8192) { Write-PMUError 'PMU.RepositoryRecoveryRequired' 'Repository receipt is oversized.' }
    try { $receipt = Get-Content -LiteralPath $safe -Raw -ErrorAction Stop | ConvertFrom-Json -ErrorAction Stop }
    catch { Write-PMUError 'PMU.RepositoryRecoveryRequired' 'Repository receipt is unreadable or incomplete.' }
    Assert-RepositoryReceipt $receipt
    if ([IO.Path]::GetFileName($safe) -cne "$($receipt.OperationId).json") { Write-PMUError 'PMU.RepositoryRecoveryRequired' 'Repository receipt name does not match its operation.' }
    $receipt
}


# Source: src/private/repository/Register-OwnedRepository.ps1
function Register-OwnedRepository {
    param([object]$Lease)
    if (@(Get-RepositoryRegistration $Lease.Name).Count) {
        Remove-Item -LiteralPath $Lease.ReceiptPath -ErrorAction Stop
        Write-PMUError 'PMU.RepositoryUnavailable' 'The random transport name already exists; it will not be overwritten.'
    }
    $parameters = @{ Name = $Lease.Name; Uri = $Lease.Source.Uri; Priority = 50; ErrorAction = 'Stop' }
    if ($Lease.Receipt.ApiVersion -ne 'Unknown') { $parameters.ApiVersion = $Lease.Receipt.ApiVersion }
    try {
        $created = @(Microsoft.PowerShell.PSResourceGet\Register-PSResourceRepository @parameters -PassThru)
    }
    catch { Write-ProviderError $_ 'PMU.RepositoryUnavailable' }
    if ($created.Count -ne 1) { Write-PMUError 'PMU.RepositoryUnavailable' 'Transport registration did not return one provider identity.' }
    Complete-OwnedRepositoryBinding $Lease $created[0]
    $registered = @(Get-RepositoryRegistration $Lease.Name)
    if ($registered.Count -ne 1) { Write-PMUError 'PMU.RepositoryUnavailable' 'Transport registration could not be verified.' }
    Assert-OwnedRepositoryConfiguration $registered[0] $Lease.Receipt
}


# Source: src/private/repository/Repair-RepositoryReceipt.ps1
function Repair-RepositoryReceipt {
    param([string]$Root)
    foreach ($entry in Get-RepositoryReceiptEntry $Root) {
        if (Test-RepositoryReceiptOwnerAlive $entry.Receipt) { continue }
        Clear-OwnedRepositoryReceipt $entry
    }
}


# Source: src/private/repository/Test-RepositoryReceiptOwnerAlive.ps1
function Test-RepositoryReceiptOwnerAlive {
    param([object]$Receipt)
    if ([long]$Receipt.OwnerPid -gt [int]::MaxValue) { Write-PMUError 'PMU.RepositoryRecoveryRequired' 'Repository receipt process identity is invalid.' }
    $started = Get-RepositoryProcessStart ([int]$Receipt.OwnerPid)
    $started -ceq $Receipt.OwnerStartedUtcTicks
}


# Source: src/private/repository/Write-BoundRepositoryReceipt.ps1
function Write-BoundRepositoryReceipt {
    param([object]$Lease)
    $path = Assert-SafePath $Lease.ReceiptPath
    $temporary = Write-RepositoryReceipt $Lease.Receipt $Lease.Root '.binding'
    try { [IO.File]::Move($temporary, $path, $true) }
    finally {
        if (Test-Path -LiteralPath $temporary) { Remove-Item -LiteralPath $temporary -ErrorAction Stop }
    }
}


# Source: src/private/repository/Write-RepositoryCleanupFailure.ps1
function Write-RepositoryCleanupFailure {
    param([System.Management.Automation.ErrorRecord]$Original)
    if ($Original -and $Original.FullyQualifiedErrorId -match '^PMU\.') {
        $Original.Exception.Data['PMU.RepositoryCleanupFailed'] = $true
        throw $Original
    }
    Write-PMUError 'PMU.RepositoryCleanupFailed' 'Owned transport cleanup failed. Preserve its receipt and retry after correcting the provider or permissions.'
}


# Source: src/private/repository/Write-RepositoryReceipt.ps1
function Write-RepositoryReceipt {
    param([object]$Receipt, [string]$Root, [string]$Suffix = '')
    $path = Assert-SafePath (Join-Path $Root "$($Receipt.OperationId).json$Suffix")
    $bytes = [Text.Encoding]::UTF8.GetBytes((ConvertTo-Json $Receipt -Depth 4 -Compress))
    $options = [IO.FileStreamOptions]::new()
    $options.Mode = [IO.FileMode]::CreateNew; $options.Access = [IO.FileAccess]::Write; $options.Share = [IO.FileShare]::None
    if (-not $IsWindows) { $options.UnixCreateMode = [IO.UnixFileMode]::UserRead -bor [IO.UnixFileMode]::UserWrite }
    $stream = [IO.FileStream]::new($path, $options)
    try { $stream.Write($bytes); $stream.Flush($true) }
    finally { $stream.Dispose() }
    $path
}


# Source: src/private/shared/Assert-MatchingProperty.ps1
function Assert-MatchingProperty {
    param([object]$Actual, [object]$Expected, [string[]]$Property, [string]$ErrorId)
    foreach ($name in $Property) {
        if ($Actual.$name -cne $Expected.$name) {
            Write-PMUError $ErrorId 'A required identity or environment precondition changed.'
        }
    }
}


# Source: src/private/shared/Assert-SafePath.ps1
function Assert-SafePath {
    param([string]$Path)
    $full = [IO.Path]::GetFullPath($Path)
    $cursor = $full
    while ($cursor) {
        if (Test-Path -LiteralPath $cursor) {
            $item = Get-Item -LiteralPath $cursor -Force -ErrorAction Stop
            if ($item.Attributes -band [IO.FileAttributes]::ReparsePoint) {
                Write-PMUError 'PMU.UnsupportedInstallation' 'Symbolic links and reparse points are not supported in controlled paths.'
            }
        }
        $cursor = [IO.Path]::GetDirectoryName($cursor)
    }
    $full.TrimEnd([IO.Path]::DirectorySeparatorChar)
}


# Source: src/private/shared/Write-PMUError.ps1
function Write-PMUError {
    param([string]$Id, [string]$Message, [string]$Guidance = 'Obtain a fresh plan after correcting the reported condition.')
    $exception = [InvalidOperationException]::new($Message)
    $target = [pscustomobject]@{ RecoveryGuidance = $Guidance }
    throw [System.Management.Automation.ErrorRecord]::new($exception, $Id, 'InvalidOperation', $target)
}


# Source: src/private/staging/Clear-StagingRoot.ps1
function Clear-StagingRoot {
    param([object]$Operation)
    $expected = Join-Path $Operation.WorkRoot 'stage'
    if ($Operation.StageRoot -cne $expected) { Write-PMUError 'PMU.RecoveryRequired' 'Refusing to clear an unexpected staging path.' }
    $null = Assert-SafePath $expected
    if (-not (Test-Path -LiteralPath $expected)) { return }
    $null = @(Get-FileTreeInventory $expected)
    Remove-Item -LiteralPath $expected -Recurse -Force -ErrorAction Stop
}


# Source: src/private/staging/Initialize-StagedPackage.ps1
function Initialize-StagedPackage {
    param([object]$Operation)
    Write-InstallationPhase $Operation.Plan 'Staging'
    Clear-StagingRoot $Operation
    Initialize-ControlledDirectory $Operation.StageRoot
    Assert-RepositoryBinding $Operation.Plan $Operation.RepositoryName
    Save-ProviderResource $Operation
    $manifest = Join-Path $Operation.StagedPath "$($Operation.Plan.ModuleName).psd1"
    Assert-PackageIdentity (Get-ManifestIdentity $manifest) $Operation.Plan
    $tree = @(Get-FileTreeInventory $Operation.StagedPath)
    if ($tree.Count -lt 2) { Write-PMUError 'PMU.VerificationFailed' 'The extracted module tree is incomplete.' }
    Assert-PackageSource $Operation.StagedPath $Operation.Plan
    $tree
}


# Source: src/private/verification/Assert-FileTreeEqual.ps1
function Assert-FileTreeEqual {
    param([object[]]$Expected, [object[]]$Actual, [string]$ErrorId = 'PMU.VerificationFailed')
    $left = ConvertTo-Json @($Expected) -Depth 4 -Compress
    $right = ConvertTo-Json @($Actual) -Depth 4 -Compress
    if ($left -cne $right) { Write-PMUError $ErrorId 'The complete file tree does not match the verified reference.' }
}


# Source: src/private/verification/Assert-InstalledPackage.ps1
function Assert-InstalledPackage {
    param([object]$Operation, [object[]]$ReferenceTree)
    $plan = $Operation.Plan
    $path = Assert-SafePath $plan.TargetInstalledPath
    $expected = Join-Path (Resolve-ModuleInstallationRoot $plan.Scope) "$($plan.ModuleName)/$($plan.TargetNumericVersion)"
    if ($path -cne $expected) { Write-PMUError 'PMU.VerificationFailed' 'The provider destination does not match the planned scope root.' }
    $manifest = Join-Path $path "$($plan.ModuleName).psd1"
    Assert-PackageIdentity (Get-ManifestIdentity $manifest) $plan
    Assert-FileTreeEqual $ReferenceTree @(Get-FileTreeInventory $path)
    Assert-PackageSource $path $plan
    $discovered = @(Get-Module -ListAvailable $manifest | Where-Object Guid -EQ $plan.ModuleGuid)
    if ($discovered.Count -ne 1) { Write-PMUError 'PMU.VerificationFailed' 'The installed module is not discoverable at its exact manifest path.' }
}


# Source: src/private/verification/Assert-PackageIdentity.ps1
function Assert-PackageIdentity {
    param([object]$Identity, [object]$Plan)
    if ($Identity.Name -cne $Plan.ModuleName -or $Identity.Version.FullVersion -cne $Plan.TargetVersion) {
        Write-PMUError 'PMU.VerificationFailed' 'The package manifest does not match the exact planned name and complete version.'
    }
    if ($Identity.Guid -ne $Plan.ModuleGuid) { Write-PMUError 'PMU.GuidMismatch' 'The package manifest GUID differs from the planned identity.' }
}


# Source: src/private/verification/Assert-PackageSource.ps1
function Assert-PackageSource {
    param([string]$Path, [object]$Plan)
    $property = Get-PackageSourceProperty $Path
    if (-not $property) { return }
    if (-not $property.Value) { return }
    if ((Assert-RepositoryUri ([uri]$property.Value)) -cne $Plan.RepositoryUri) {
        Write-PMUError 'PMU.VerificationFailed' 'Package source metadata does not match the planned repository.'
    }
}


# Source: src/private/verification/Get-FileTreeInventory.ps1
function Get-FileTreeInventory {
    param([string]$Path)
    $root = Assert-SafePath $Path
    foreach ($item in Get-ChildItem -LiteralPath $root -Recurse -Force -ErrorAction Stop | Sort-Object FullName -CaseSensitive) {
        $null = Assert-SafePath $item.FullName
        $relative = [IO.Path]::GetRelativePath($root, $item.FullName).Replace('\', '/')
        if ($item.PSIsContainer) {
            [pscustomobject]@{ Path = "$relative/"; Length = 0; SHA256 = '' }
            continue
        }
        # PSResourceGet intentionally generates this root-level installation metadata file.
        if ($relative -ceq 'PSGetModuleInfo.xml') { continue }
        [pscustomobject]@{ Path = $relative; Length = $item.Length; SHA256 = (Get-FileHash -LiteralPath $item.FullName -Algorithm SHA256).Hash }
    }
}


# Source: src/private/verification/Get-PackageSourceProperty.ps1
function Get-PackageSourceProperty {
    param([string]$Path)
    $metadataPath = Join-Path $Path 'PSGetModuleInfo.xml'
    if (-not (Test-Path -LiteralPath $metadataPath)) { return }
    try { $metadata = Import-Clixml -LiteralPath $metadataPath -ErrorAction Stop }
    catch { Write-PMUError 'PMU.VerificationFailed' 'Provider source metadata is unreadable.' }
    $metadata.PSObject.Properties['RepositorySourceLocation']
}


# Source: src/private/version/Compare-CompleteModuleVersion.ps1
function Compare-CompleteModuleVersion {
    param([string]$Left, [string]$Right)
    $a = ConvertTo-CompleteModuleVersion $Left
    $b = ConvertTo-CompleteModuleVersion $Right
    $comparison = ([version]$a.NumericVersion).CompareTo([version]$b.NumericVersion)
    if ($comparison) { return [Math]::Sign($comparison) }
    Compare-PrereleaseLabel $a.Prerelease $b.Prerelease
}


# Source: src/private/version/Compare-PrereleaseIdentifier.ps1
function Compare-PrereleaseIdentifier {
    param([string]$Left, [string]$Right)
    $leftNumeric = $Left -match '^\d+$'
    $rightNumeric = $Right -match '^\d+$'
    if ($leftNumeric -and $rightNumeric) {
        return [Math]::Sign(([System.Numerics.BigInteger]::Parse($Left)).CompareTo([System.Numerics.BigInteger]::Parse($Right)))
    }
    if ($leftNumeric -ne $rightNumeric) {
        if ($leftNumeric) { return -1 }
        return 1
    }
    [Math]::Sign([string]::CompareOrdinal($Left, $Right))
}


# Source: src/private/version/Compare-PrereleaseLabel.ps1
function Compare-PrereleaseLabel {
    param([string]$Left, [string]$Right)
    if ($Left -ceq $Right) { return 0 }
    if (-not $Left) { return 1 }
    if (-not $Right) { return -1 }
    Compare-PrereleaseSegmentList $Left.Split('.') $Right.Split('.')
}


# Source: src/private/version/Compare-PrereleaseSegmentList.ps1
function Compare-PrereleaseSegmentList {
    param([string[]]$Left, [string[]]$Right)
    for ($index = 0; $index -lt [Math]::Min($Left.Count, $Right.Count); $index++) {
        $comparison = Compare-PrereleaseIdentifier $Left[$index] $Right[$index]
        if ($comparison) { return $comparison }
    }
    [Math]::Sign($Left.Count - $Right.Count)
}


# Source: src/private/version/ConvertTo-CompleteModuleVersion.ps1
function ConvertTo-CompleteModuleVersion {
    param([string]$Version)
    if ($Version -notmatch '^(?<numeric>\d+\.\d+(?:\.\d+){0,2})(?:-(?<label>[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$') {
        Write-PMUError 'PMU.UnsupportedProviderBehavior' 'The module version has an unsupported form.'
    }
    $numeric = [version]$Matches.numeric
    $label = $Matches['label']
    if (@($label -split '\.' | Where-Object { $_ -match '^0\d+$' }).Count) {
        Write-PMUError 'PMU.UnsupportedProviderBehavior' 'Numeric prerelease identifiers cannot have leading zeros.'
    }
    [pscustomobject]@{ FullVersion = $Version; NumericVersion = $numeric.ToString(); Prerelease = "$label"; IsPrerelease = [bool]$label }
}