Eigenverft.Manifested.Drydock.Process.ps1

function Open-UrlInBrowser {
<#
.SYNOPSIS
Opens local HTML files or web URLs using the default or a selected browser on Windows, macOS, and Linux (PS 5.1 and PS 7+).
 
.DESCRIPTION
Resolves each provided input to either a local file or a web URL (http/https).
Uses the platform’s default opener when -Browser 'Default' is chosen; otherwise tries the selected browser.
Fails fast with actionable errors when required external tools are unavailable (macOS 'open', Linux 'xdg-open')
or when a specific browser was requested but not found.
Designed to be idempotent and minimally chatty; emits a single Write-Host line per opened target.
 
.PARAMETER Path
One or more file paths or URLs. Supports absolute and relative file paths, as well as http/https/file URIs.
 
.PARAMETER Wait
Waits for the launched process (or app on macOS) to exit before returning.
 
.PARAMETER Browser
Which browser to use. 'Default' uses the OS default; otherwise a specific browser is attempted.
Supported values: Default, Edge, Chrome, Firefox, Safari (Safari is macOS only).
 
.PARAMETER BrowserPath
Explicit path or command to a browser. On macOS, this may be either an app name/path suitable for 'open -a'
(e.g., 'Safari' or '/Applications/Firefox.app') or a direct binary path
(e.g., '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome').
 
.EXAMPLE
Open-UrlInBrowser 'https://example.org'
Opens the URL with the default browser on the current platform.
 
.EXAMPLE
Open-UrlInBrowser -Path '.\docs\index.html' -Wait
Opens a local HTML file with the default browser and waits until the browser process (or app) closes.
 
.EXAMPLE
# Windows-only example
Open-UrlInBrowser -Path 'https://contoso.com' -Browser Edge
Opens the URL explicitly with Microsoft Edge on Windows; fails fast if Edge is not available.
 
.EXAMPLE
# macOS-only example
Open-UrlInBrowser -Path 'https://contoso.com' -Browser Safari
Opens the URL with Safari via 'open -a'; fails fast if 'open' is unavailable.
 
.EXAMPLE
# Linux example
Open-UrlInBrowser -Path '/home/user/site/index.html' -Browser Firefox
Opens a local file in Firefox on Linux; fails fast if Firefox is not found.
 
.NOTES
- Idempotent: no stateful changes are made; repeated invocations produce the same external action.
- Logging: emits a concise Write-Host per open action only.
- No SupportsShouldProcess and no pipeline binding by design (per policy).
#>

    [CmdletBinding()] # Intentionally omits SupportsShouldProcess per policy
    param(
        [Parameter(Mandatory, Position = 0)]
        [Alias('FullName','LiteralPath')]
        [ValidateNotNullOrEmpty()]
        [string[]] $Path,

        [switch] $Wait,

        [ValidateSet('Default','Edge','Chrome','Firefox','Safari')]
        [string] $Browser = 'Default',

        [string] $BrowserPath
    )

    # --- Inline helpers (local scope, deterministic, no pipeline writes) ---

    function local:_Get-Platform {
        [Diagnostics.CodeAnalysis.SuppressMessage("PSUseApprovedVerbs","")]
        param()
        # Use RuntimeInformation when available (PS7+/newer frameworks). Fallback to env vars.
        $ri = [type]::GetType('System.Runtime.InteropServices.RuntimeInformation')
        if ($ri) {
            $win = [System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([System.Runtime.InteropServices.OSPlatform]::Windows)
            $osx = [System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([System.Runtime.InteropServices.OSPlatform]::OSX)
            $lin = [System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([System.Runtime.InteropServices.OSPlatform]::Linux)
        } else {
            $win = ($env:OS -eq 'Windows_NT')
            $osx = $false
            $lin = -not $win
        }
        return [PSCustomObject]@{ Windows = $win; MacOS = $osx; Linux = $lin }
    }

    function local:_Resolve-Target {
        [Diagnostics.CodeAnalysis.SuppressMessage("PSUseApprovedVerbs","")]
        param([Parameter(Mandatory)][string]$InputPath)

        # Prefer URL if clearly absolute and http/https/file
        $u = $null
        if ([Uri]::TryCreate($InputPath, [UriKind]::Absolute, [ref]$u)) {
            if ($u.Scheme -eq 'file') { return [PSCustomObject]@{ Kind='File'; Value=$u.LocalPath } }
            if ($u.Scheme -in @('http','https')) { return [PSCustomObject]@{ Kind='Web';  Value=$InputPath } }
        }

        # Resolve local file path; reject directories, fail clearly if missing
        try {
            $resolved = Resolve-Path -LiteralPath $InputPath -ErrorAction Stop
            $item = Get-Item -LiteralPath $resolved.ProviderPath -ErrorAction Stop
        } catch {
            throw "File not found or invalid URL: $InputPath"
        }
        if ($item.PSIsContainer) { throw "Expected a file but got a directory: $InputPath" }
        return [PSCustomObject]@{ Kind='File'; Value=$item.FullName }
    }

    function local:_Ensure-External {
        [Diagnostics.CodeAnalysis.SuppressMessage("PSUseApprovedVerbs","")]
        param(
            [Parameter(Mandatory)][bool]$OnMac,
            [Parameter(Mandatory)][bool]$OnLinux
        )
        if ($OnMac) {
            if (-not (Get-Command -Name 'open' -ErrorAction SilentlyContinue)) {
                throw "Missing required tool 'open'. On macOS, ensure command-line tools are available (the 'open' utility is standard)."
            }
        }
        if ($OnLinux) {
            if (-not (Get-Command -Name 'xdg-open' -ErrorAction SilentlyContinue)) {
                throw "Missing required tool 'xdg-open'. Install package 'xdg-utils' via your distro's package manager."
            }
        }
    }

    function local:_Get-BrowserCommand {
        [Diagnostics.CodeAnalysis.SuppressMessage("PSUseApprovedVerbs","")]
        param(
            [Parameter(Mandatory)][string]$Browser,
            [Parameter(Mandatory)][bool]$OnWindows,
            [Parameter(Mandatory)][bool]$OnMac,
            [Parameter(Mandatory)][bool]$OnLinux,
            [string]$BrowserPath
        )

        # Explicit path/command preferred when provided
        if ($BrowserPath) {
            if ($OnMac -and ($BrowserPath -like '*.app' -or $BrowserPath -eq 'Safari' -or $BrowserPath -eq 'Firefox' -or $BrowserPath -eq 'Google Chrome')) {
                return [PSCustomObject]@{ Mode='MacApp'; App=$BrowserPath }
            }
            # For direct binaries or commands, try to resolve presence when possible
            $cmd = Get-Command -Name $BrowserPath -ErrorAction SilentlyContinue
            if ($cmd) { return [PSCustomObject]@{ Mode='Exe'; Path=$cmd.Source } }
            if (Test-Path -LiteralPath $BrowserPath) { return [PSCustomObject]@{ Mode='Exe'; Path=$BrowserPath } }
            throw "BrowserPath '$BrowserPath' not found. Provide a valid app/binary path or command."
        }

        if ($Browser -eq 'Default') { return [PSCustomObject]@{ Mode='Default' } }

        if ($OnWindows) {
            $cands = switch ($Browser) {
                'Edge'    { @('msedge.exe', "$env:ProgramFiles\Microsoft\Edge\Application\msedge.exe", "$env:ProgramFiles(x86)\Microsoft\Edge\Application\msedge.exe") }
                'Chrome'  { @('chrome.exe', "$env:ProgramFiles\Google\Chrome\Application\chrome.exe", "$env:ProgramFiles(x86)\Google\Chrome\Application\chrome.exe") }
                'Firefox' { @('firefox.exe', "$env:ProgramFiles\Mozilla Firefox\firefox.exe", "$env:ProgramFiles(x86)\Mozilla Firefox\firefox.exe") }
                default   { @() }
            }
            foreach ($c in $cands) {
                $cmd = Get-Command -Name $c -ErrorAction SilentlyContinue
                if ($cmd) { return [PSCustomObject]@{ Mode='Exe'; Path=$cmd.Source } }
                if ([IO.Path]::IsPathRooted($c) -and (Test-Path -LiteralPath $c)) { return [PSCustomObject]@{ Mode='Exe'; Path=$c } }
            }
            throw "Requested browser '$Browser' was not found on Windows. Install it or use -BrowserPath."
        }

        if ($OnMac) {
            $app = switch ($Browser) {
                'Safari'  { 'Safari' }
                'Chrome'  { 'Google Chrome' }
                'Firefox' { 'Firefox' }
                default   { $null }
            }
            if (-not $app) { throw "Requested browser '$Browser' is not available on macOS." }
            return [PSCustomObject]@{ Mode='MacApp'; App=$app }
        }

        if ($OnLinux) {
            $cands = switch ($Browser) {
                'Chrome'  { @('google-chrome','google-chrome-stable','chromium-browser','chromium') }
                'Firefox' { @('firefox') }
                'Edge'    { @('microsoft-edge','microsoft-edge-stable') }
                default   { @() }
            }
            foreach ($c in $cands) {
                $cmd = Get-Command -Name $c -ErrorAction SilentlyContinue
                if ($cmd) { return [PSCustomObject]@{ Mode='Exe'; Path=$cmd.Source } }
            }
            throw "Requested browser '$Browser' was not found on Linux. Install it or use -BrowserPath."
        }

        return [PSCustomObject]@{ Mode='Default' }
    }

    # --- Execution ---
    $plat = local:_Get-Platform
    local:_Ensure-External -OnMac:$($plat.MacOS) -OnLinux:$($plat.Linux)

    foreach ($raw in $Path) {
        $t = local:_Resolve-Target -InputPath $raw
        $cmd = local:_Get-BrowserCommand -Browser $Browser -OnWindows:$($plat.Windows) -OnMac:$($plat.MacOS) -OnLinux:$($plat.Linux) -BrowserPath $BrowserPath

        if ($cmd.Mode -eq 'Default') {
            if ($plat.Windows) {
                if ($Wait) { Start-Process -FilePath $t.Value -Wait } else { Start-Process -FilePath $t.Value | Out-Null }
            } elseif ($plat.MacOS) {
                $cmdArgs = @($t.Value)
                if ($Wait) { $cmdArgs = @('-W') + $cmdArgs }
                Start-Process -FilePath 'open' -ArgumentList $cmdArgs | Out-Null
            } elseif ($plat.Linux) {
                if ($Wait) { Start-Process -FilePath 'xdg-open' -ArgumentList @($t.Value) -Wait } else { Start-Process -FilePath 'xdg-open' -ArgumentList @($t.Value) | Out-Null }
            } else {
                if ($Wait) { Start-Process -FilePath $t.Value -Wait } else { Start-Process -FilePath $t.Value | Out-Null }
            }
            Write-Host ("Opening {0} with default browser: {1}" -f $t.Kind, $t.Value)
            continue
        }

        if ($plat.MacOS -and $cmd.Mode -eq 'MacApp') {
            $cmdArgs = @('-a', $cmd.App, $t.Value)
            if ($Wait) { $cmdArgs = @('-W') + $cmdArgs }
            Start-Process -FilePath 'open' -ArgumentList $cmdArgs | Out-Null
            Write-Host ("Opening {0} with {1}: {2}" -f $t.Kind, $cmd.App, $t.Value)
            continue
        }

        # Executable path or resolved command on any platform
        $exe = $cmd.Path
        if (-not $exe) { throw "Internal resolution error: browser command could not be determined." }
        if ($Wait) { Start-Process -FilePath $exe -ArgumentList @($t.Value) -Wait } else { Start-Process -FilePath $exe -ArgumentList @($t.Value) | Out-Null }
        Write-Host ("Opening {0} with {1}: {2}" -f $t.Kind, $exe, $t.Value)
    }
}