GitHub.psm1

[Cmdletbinding()]
param()

$scriptName = $MyInvocation.MyCommand.Name
Write-Verbose "[$scriptName] Importing subcomponents"

#region - Data import
Write-Verbose "[$scriptName] - [data] - Processing folder"
$dataFolder = (Join-Path $PSScriptRoot 'data')
Write-Verbose "[$scriptName] - [data] - [$dataFolder]"
Get-ChildItem -Path "$dataFolder" -Recurse -Force -Include '*.psd1' -ErrorAction SilentlyContinue | ForEach-Object {
    Write-Verbose "[$scriptName] - [data] - [$($_.Name)] - Importing"
    New-Variable -Name $_.BaseName -Value (Import-PowerShellDataFile -Path $_.FullName) -Force
    Write-Verbose "[$scriptName] - [data] - [$($_.Name)] - Done"
}

Write-Verbose "[$scriptName] - [data] - Done"
#endregion - Data import

#region - From private
Write-Verbose "[$scriptName] - [private] - Processing folder"

#region - From private/Auth
Write-Verbose "[$scriptName] - [private/Auth] - Processing folder"

#region - From private/Auth/DeviceFlow
Write-Verbose "[$scriptName] - [private/Auth/DeviceFlow] - Processing folder"

#region - From private/Auth/DeviceFlow/Invoke-GitHubDeviceFlowLogin.ps1
Write-Verbose "[$scriptName] - [private/Auth/DeviceFlow/Invoke-GitHubDeviceFlowLogin.ps1] - Importing"

function Invoke-GitHubDeviceFlowLogin {
    <#
        .SYNOPSIS
        Starts the GitHub Device Flow login process.

        .DESCRIPTION
        Starts the GitHub Device Flow login process. This will prompt the user to visit a URL and enter a code.

        .EXAMPLE
        Invoke-GitHubDeviceFlowLogin

        This will start the GitHub Device Flow login process.
        The user gets prompted to visit a URL and enter a code.

        .NOTES
        For more info about the Device Flow visit:
        https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/building-a-cli-with-a-github-app
        https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps#device-flow
    #>

    [OutputType([void])]
    [CmdletBinding()]
    param(
        # The Client ID of the GitHub App.
        [Parameter(Mandatory)]
        [string] $ClientID,

        # The scope of the access token, when using OAuth authentication.
        # Provide the list of scopes as space-separated values.
        # For more information on scopes visit:
        # https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps
        [Parameter()]
        [string] $Scope,

        # The refresh token to use for re-authentication.
        [Parameter()]
        [securestring] $RefreshToken
    )

    do {
        if ($RefreshToken) {
            $tokenResponse = Wait-GitHubAccessToken -ClientID $ClientID -RefreshToken $RefreshToken
        } else {
            $deviceCodeResponse = Request-GitHubDeviceCode -ClientID $ClientID -Scope $Scope

            $deviceCode = $deviceCodeResponse.device_code
            $interval = $deviceCodeResponse.interval
            $userCode = $deviceCodeResponse.user_code
            $verificationUri = $deviceCodeResponse.verification_uri

            Write-Host '! ' -ForegroundColor DarkYellow -NoNewline
            Write-Host "We added the code to your clipboard: [$userCode]"
            $userCode | Set-Clipboard
            Read-Host 'Press Enter to open github.com in your browser...'
            Start-Process $verificationUri

            $tokenResponse = Wait-GitHubAccessToken -DeviceCode $deviceCode -ClientID $ClientID -Interval $interval
        }
    } while ($tokenResponse.error)
    $tokenResponse
}

Write-Verbose "[$scriptName] - [private/Auth/DeviceFlow/Invoke-GitHubDeviceFlowLogin.ps1] - Done"
#endregion - From private/Auth/DeviceFlow/Invoke-GitHubDeviceFlowLogin.ps1
#region - From private/Auth/DeviceFlow/Request-GitHubAccessToken.ps1
Write-Verbose "[$scriptName] - [private/Auth/DeviceFlow/Request-GitHubAccessToken.ps1] - Importing"

function Request-GitHubAccessToken {
    <#
        .SYNOPSIS
        Request a GitHub token using the Device Flow.

        .DESCRIPTION
        Request a GitHub token using the Device Flow.
        This will poll the GitHub API until the user has entered the code.

        .EXAMPLE
        Request-GitHubAccessToken -DeviceCode $deviceCode -ClientID $ClientID

        This will poll the GitHub API until the user has entered the code.

        .NOTES
        For more info about the Device Flow visit:
        https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/building-a-cli-with-a-github-app
    #>

    [OutputType([PSCustomObject])]
    [CmdletBinding(DefaultParameterSetName = 'DeviceFlow')]
    param(
        # The Client ID of the GitHub App.
        [Parameter(Mandatory)]
        [string] $ClientID,

        # The 'device_code' used to request the access token.
        [Parameter(
            Mandatory,
            ParameterSetName = 'DeviceFlow'
        )]
        [string] $DeviceCode,

        # The refresh token used create a new access token.
        [Parameter(
            Mandatory,
            ParameterSetName = 'RefreshToken'
        )]
        [securestring] $RefreshToken
    )

    $body = @{
        'client_id' = $ClientID
    }

    if ($PSBoundParameters.ContainsKey('RefreshToken')) {
        $body += @{
            'refresh_token' = (ConvertFrom-SecureString $RefreshToken -AsPlainText)
            'grant_type'    = 'refresh_token'
        }
    }

    if ($PSBoundParameters.ContainsKey('DeviceCode')) {
        $body += @{
            'device_code' = $DeviceCode
            'grant_type'  = 'urn:ietf:params:oauth:grant-type:device_code'
        }
    }

    $RESTParams = @{
        Uri     = 'https://github.com/login/oauth/access_token'
        Method  = 'POST'
        Body    = $body
        Headers = @{ 'Accept' = 'application/json' }
    }

    try {
        Write-Verbose ($RESTParams.GetEnumerator() | Out-String)

        $tokenResponse = Invoke-RestMethod @RESTParams -Verbose:$false

        Write-Verbose ($tokenResponse | ConvertTo-Json | Out-String)
        return $tokenResponse
    } catch {
        Write-Error $_
        throw $_
    }
}

Write-Verbose "[$scriptName] - [private/Auth/DeviceFlow/Request-GitHubAccessToken.ps1] - Done"
#endregion - From private/Auth/DeviceFlow/Request-GitHubAccessToken.ps1
#region - From private/Auth/DeviceFlow/Request-GitHubDeviceCode.ps1
Write-Verbose "[$scriptName] - [private/Auth/DeviceFlow/Request-GitHubDeviceCode.ps1] - Importing"

function Request-GitHubDeviceCode {
    <#
        .SYNOPSIS
        Request a GitHub Device Code.

        .DESCRIPTION
        Request a GitHub Device Code.

        .EXAMPLE
        Request-GitHubDeviceCode -ClientID $ClientID -Mode $Mode

        This will request a GitHub Device Code.

        .NOTES
        For more info about the Device Flow visit:
        https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/building-a-cli-with-a-github-app
    #>

    [OutputType([PSCustomObject])]
    [CmdletBinding()]
    param(
        # The Client ID of the GitHub App.
        [Parameter(Mandatory)]
        [string] $ClientID,

        # The scope of the access token, when using OAuth authentication.
        # Provide the list of scopes as space-separated values.
        # For more information on scopes visit:
        # https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps
        [Parameter()]
        [string] $Scope = 'gist, read:org, repo, workflow'
    )

    $headers = @{
        Accept = 'application/json'
    }

    $body = @{
        client_id = $ClientID
        scope     = $Scope
    }

    $RESTParams = @{
        Uri     = 'https://github.com/login/device/code'
        Method  = 'POST'
        Body    = $body
        Headers = $headers
    }

    try {
        Write-Verbose ($RESTParams.GetEnumerator() | Out-String)

        $deviceCodeResponse = Invoke-RestMethod @RESTParams -Verbose:$false
        return $deviceCodeResponse
    } catch {
        Write-Error $_
        throw $_
    }
}

Write-Verbose "[$scriptName] - [private/Auth/DeviceFlow/Request-GitHubDeviceCode.ps1] - Done"
#endregion - From private/Auth/DeviceFlow/Request-GitHubDeviceCode.ps1
#region - From private/Auth/DeviceFlow/Wait-GitHubAccessToken.ps1
Write-Verbose "[$scriptName] - [private/Auth/DeviceFlow/Wait-GitHubAccessToken.ps1] - Importing"

function Wait-GitHubAccessToken {
    <#
        .SYNOPSIS
        Waits for the GitHub Device Flow to complete.

        .DESCRIPTION
        Waits for the GitHub Device Flow to complete.
        This will poll the GitHub API until the user has entered the code.

        .EXAMPLE
        Wait-GitHubAccessToken -DeviceCode $deviceCode -ClientID $ClientID -Interval $interval

        This will poll the GitHub API until the user has entered the code.

        .EXAMPLE
        Wait-GitHubAccessToken -Refresh -ClientID $ClientID

        .NOTES
        For more info about the Device Flow visit:
        https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/building-a-cli-with-a-github-app
    #>

    [OutputType([PSCustomObject])]
    [CmdletBinding(DefaultParameterSetName = 'DeviceFlow')]
    param(
        # The Client ID of the GitHub App.
        [Parameter(Mandatory)]
        [string] $ClientID,

        # The device code used to request the access token.
        [Parameter(
            Mandatory,
            ParameterSetName = 'DeviceFlow'
        )]
        [string] $DeviceCode,

        # The refresh token used to request a new access token.
        [Parameter(
            Mandatory,
            ParameterSetName = 'RefreshToken'
        )]
        [securestring] $RefreshToken,

        # The interval to wait between polling for the token.
        [Parameter()]
        [int] $Interval = 5

    )

    do {
        if ($RefreshToken) {
            $response = Request-GitHubAccessToken -ClientID $ClientID -RefreshToken $RefreshToken
        } else {
            $response = Request-GitHubAccessToken -ClientID $ClientID -DeviceCode $DeviceCode
        }
        if ($response.error) {
            switch ($response.error) {
                'authorization_pending' {
                    # The user has not yet entered the code.
                    # Wait, then poll again.
                    Write-Verbose $response.error_description
                    Start-Sleep -Seconds $interval
                    continue
                }
                'slow_down' {
                    # The app polled too fast.
                    # Wait for the interval plus 5 seconds, then poll again.
                    Write-Verbose $response.error_description
                    Start-Sleep -Seconds ($interval + 5)
                    continue
                }
                'expired_token' {
                    # The 'device_code' expired, and the process needs to restart.
                    Write-Error $response.error_description
                    exit 1
                }
                'unsupported_grant_type' {
                    # The 'grant_type' is not supported.
                    Write-Error $response.error_description
                    exit 1
                }
                'incorrect_client_credentials' {
                    # The 'client_id' is not valid.
                    Write-Error $response.error_description
                    exit 1
                }
                'incorrect_device_code' {
                    # The 'device_code' is not valid.
                    Write-Error $response.error_description
                    exit 2
                }
                'access_denied' {
                    # The user cancelled the process. Stop polling.
                    Write-Error $response.error_description
                    exit 1
                }
                'device_flow_disabled' {
                    # The GitHub App does not support the Device Flow.
                    Write-Error $response.error_description
                    exit 1
                }
                default {
                    # The response contains an access token. Stop polling.
                    Write-Error 'Unknown error:'
                    Write-Error $response.error
                    Write-Error $response.error_description
                    Write-Error $response.error_uri
                    break
                }
            }
        }
    } until ($response.access_token)
    $response
}

Write-Verbose "[$scriptName] - [private/Auth/DeviceFlow/Wait-GitHubAccessToken.ps1] - Done"
#endregion - From private/Auth/DeviceFlow/Wait-GitHubAccessToken.ps1

Write-Verbose "[$scriptName] - [private/Auth/DeviceFlow] - Done"
#endregion - From private/Auth/DeviceFlow


Write-Verbose "[$scriptName] - [private/Auth] - Done"
#endregion - From private/Auth

#region - From private/Config
Write-Verbose "[$scriptName] - [private/Config] - Processing folder"

#region - From private/Config/Initialize-SecretVault.ps1
Write-Verbose "[$scriptName] - [private/Config/Initialize-SecretVault.ps1] - Importing"

#Requires -Version 7.0
#Requires -Modules Microsoft.PowerShell.SecretManagement
#Requires -Modules Microsoft.PowerShell.SecretStore

function Initialize-SecretVault {
    <#
    .SYNOPSIS
    Initialize a secret vault.

    .DESCRIPTION
    Initialize a secret vault. If the vault does not exist, it will be created.

    .EXAMPLE
    Initialize-SecretVault -Name 'SecretStore' -Type 'Microsoft.PowerShell.SecretStore'

    Initializes a secret vault named 'SecretStore' using the 'Microsoft.PowerShell.SecretStore' module.

    .NOTES
    For more information about secret vaults, see https://learn.microsoft.com/en-us/powershell/utility-modules/secretmanagement/overview?view=ps-modules
    #>


    [OutputType([void])]
    [CmdletBinding()]
    param (
        # The name of the secret vault.
        [Parameter()]
        [string] $Name = 'SecretStore',

        # The type of the secret vault.
        [Parameter()]
        [Alias('ModuleName')]
        [string] $Type = 'Microsoft.PowerShell.SecretStore'
    )

    $functionName = $MyInvocation.MyCommand.Name

    $vault = Get-SecretVault | Where-Object { $_.ModuleName -eq $Type }
    if (-not $vault) {
        Write-Verbose "[$functionName] - [$Type] - Registering"

        switch ($Type) {
            'Microsoft.PowerShell.SecretStore' {
                $vaultParameters = @{
                    Authentication  = 'None'
                    PasswordTimeout = -1
                    Interaction     = 'None'
                    Scope           = 'CurrentUser'
                    WarningAction   = 'SilentlyContinue'
                    Confirm         = $false
                    Force           = $true
                }
                Reset-SecretStore @vaultParameters
            }
        }
        Write-Verbose "[$functionName] - [$Type] - Done"
    } else {
        Write-Verbose "[$functionName] - [$Type] - already registered"
    }

    $secretStore = Get-SecretVault | Where-Object { $_.Name -eq $Name }
    if (-not $secretStore) {
        Write-Verbose "[$functionName] - [$Name] - Registering"
        $secretVault = @{
            Name         = $Name
            ModuleName   = $Type
            DefaultVault = $true
            Description  = 'SecretStore'
        }
        Register-SecretVault @secretVault
        Write-Verbose "[$functionName] - [$Name] - Done"
    } else {
        Write-Verbose "[$functionName] - [$Name] - already registered"
    }
}

Write-Verbose "[$scriptName] - [private/Config/Initialize-SecretVault.ps1] - Done"
#endregion - From private/Config/Initialize-SecretVault.ps1
#region - From private/Config/Reset-GitHubConfig.ps1
Write-Verbose "[$scriptName] - [private/Config/Reset-GitHubConfig.ps1] - Importing"

function Reset-GitHubConfig {
    <#
        .SYNOPSIS
        Reset the GitHub configuration.

        .DESCRIPTION
        Reset the GitHub configuration. Specific scopes can be reset by using the Scope parameter.

        .EXAMPLE
        Reset-GitHubConfig

        Resets the entire GitHub configuration.

        .EXAMPLE
        Reset-GitHubConfig -Scope 'Auth'

        Resets the Auth related variables of the GitHub configuration.
    #>

    [Alias('Reset-GHConfig')]
    [OutputType([void])]
    [CmdletBinding()]
    param(
        # Reset the GitHub configuration for a specific scope.
        [Parameter()]
        [ValidateSet('Auth', 'All')]
        [string] $Scope = 'All'
    )

    Write-Verbose "Resetting GitHub configuration for scope '$Scope'..."
    switch ($Scope) {
        'Auth' {
            $Settings = @{
                AccessToken                = [securestring]::new()
                AccessTokenExpirationDate  = [datetime]::MinValue
                AccessTokenType            = ''
                AuthType                   = ''
                DeviceFlowType             = ''
                RefreshToken               = [securestring]::new()
                RefreshTokenExpirationDate = [datetime]::MinValue
                Scope                      = ''
            }
        }
        'All' {
            $Settings = @{
                AccessToken                = [securestring]::new()
                AccessTokenExpirationDate  = [datetime]::MinValue
                AccessTokenType            = ''
                ApiBaseUri                 = 'https://api.github.com'
                ApiVersion                 = '2022-11-28'
                AuthType                   = ''
                DeviceFlowType             = ''
                Owner                      = ''
                RefreshToken               = [securestring]::new()
                RefreshTokenExpirationDate = [datetime]::MinValue
                Repo                       = ''
                Scope                      = ''
                UserName                   = ''
            }
        }
    }
    Set-GitHubConfig @Settings
}

Write-Verbose "[$scriptName] - [private/Config/Reset-GitHubConfig.ps1] - Done"
#endregion - From private/Config/Reset-GitHubConfig.ps1

Write-Verbose "[$scriptName] - [private/Config] - Done"
#endregion - From private/Config

#region - From private/Utilities
Write-Verbose "[$scriptName] - [private/Utilities] - Processing folder"

#region - From private/Utilities/ConvertFrom-HashTable.ps1
Write-Verbose "[$scriptName] - [private/Utilities/ConvertFrom-HashTable.ps1] - Importing"

function ConvertFrom-HashTable {
    [CmdletBinding()]
    param (
        [Parameter(
            Mandatory,
            ValueFromPipeline
        )]
        [object]$InputObject
    )
    ([pscustomobject](@{} + $InputObject))
}

Write-Verbose "[$scriptName] - [private/Utilities/ConvertFrom-HashTable.ps1] - Done"
#endregion - From private/Utilities/ConvertFrom-HashTable.ps1
#region - From private/Utilities/ConvertTo-HashTable.ps1
Write-Verbose "[$scriptName] - [private/Utilities/ConvertTo-HashTable.ps1] - Importing"

function ConvertTo-HashTable {
    [CmdletBinding()]
    param (
        [Parameter(
            Mandatory,
            ValueFromPipeline
        )]
        [pscustomobject]$InputObject
    )
    [hashtable]$hashtable = @{}

    foreach ($item in $InputObject.PSobject.Properties) {
        Write-Verbose "$($item.Name) : $($item.Value) : $($item.TypeNameOfValue)"
        $hashtable.$($item.Name) = $item.Value
    }
    $hashtable
}

Write-Verbose "[$scriptName] - [private/Utilities/ConvertTo-HashTable.ps1] - Done"
#endregion - From private/Utilities/ConvertTo-HashTable.ps1
#region - From private/Utilities/Join-Hashtable.ps1
Write-Verbose "[$scriptName] - [private/Utilities/Join-Hashtable.ps1] - Importing"

function Join-Hashtable {
    [OutputType([void])]
    [Alias('Merge-HashTable')]
    [CmdletBinding()]
    param (
        [hashtable] $Main,
        [hashtable] $Overrides
    )
    $hashtable = @{}
    $Main.Keys | ForEach-Object {
        $hashtable[$_] = $Main[$_]
    }
    $Overrides.Keys | ForEach-Object {
        $hashtable[$_] = $Overrides[$_]
    }
    $hashtable
}

Write-Verbose "[$scriptName] - [private/Utilities/Join-Hashtable.ps1] - Done"
#endregion - From private/Utilities/Join-Hashtable.ps1
#region - From private/Utilities/Remove-HashTableEntries.ps1
Write-Verbose "[$scriptName] - [private/Utilities/Remove-HashTableEntries.ps1] - Importing"

function Remove-HashtableEntries {
    [OutputType([void])]
    [CmdletBinding()]
    param (
        [Parameter(
            Mandatory,
            ValueFromPipeline
        )]
        [hashtable] $Hashtable,
        [Parameter()]
        [switch] $NullOrEmptyValues,
        [Parameter()]
        [string[]] $RemoveTypes,
        [Parameter()]
        [string[]] $RemoveNames,
        [Parameter()]
        [string[]] $KeepTypes,
        [Parameter()]
        [string[]] $KeepNames

    )
    if ($NullOrEmptyValues) {
        Write-Verbose 'Remove keys with null or empty values'
        ($Hashtable.GetEnumerator() | Where-Object { -not $_.Value }) | ForEach-Object {
            Write-Verbose " - [$($_.Name)] - Value: [$($_.Value)] - Remove"
            $Hashtable.Remove($_.Name)
        }
    }
    if ($RemoveTypes) {
        Write-Verbose "Remove keys of type: [$RemoveTypes]"
        ($Hashtable.GetEnumerator() | Where-Object { ($_.Value.GetType().Name -in $RemoveTypes) }) | ForEach-Object {
            Write-Verbose " - [$($_.Name)] - Type: [$($_.Value.GetType().Name)] - Remove"
            $Hashtable.Remove($_.Name)
        }
    }
    if ($KeepTypes) {
        Write-Verbose "Remove keys NOT of type: [$KeepTypes]"
        ($Hashtable.GetEnumerator() | Where-Object { ($_.Value.GetType().Name -notin $KeepTypes) }) | ForEach-Object {
            Write-Verbose " - [$($_.Name)] - Type: [$($_.Value.GetType().Name)] - Remove"
            $Hashtable.Remove($_.Name)
        }
    }
    if ($RemoveNames) {
        Write-Verbose "Remove keys named: [$RemoveNames]"
        ($Hashtable.GetEnumerator() | Where-Object { $_.Name -in $RemoveNames }) | ForEach-Object {
            Write-Verbose " - [$($_.Name)] - Remove"
            $Hashtable.Remove($_.Name)
        }
    }
    if ($KeepNames) {
        Write-Verbose "Remove keys NOT named: [$KeepNames]"
        ($Hashtable.GetEnumerator() | Where-Object { $_.Name -notin $KeepNames }) | ForEach-Object {
            Write-Verbose " - [$($_.Name)] - Remove"
            $Hashtable.Remove($_.Name)
        }
    }
}

Write-Verbose "[$scriptName] - [private/Utilities/Remove-HashTableEntries.ps1] - Done"
#endregion - From private/Utilities/Remove-HashTableEntries.ps1

Write-Verbose "[$scriptName] - [private/Utilities] - Done"
#endregion - From private/Utilities


Write-Verbose "[$scriptName] - [private] - Done"
#endregion - From private

#region - From public
Write-Verbose "[$scriptName] - [public] - Processing folder"

#region - From public/API
Write-Verbose "[$scriptName] - [public/API] - Processing folder"

#region - From public/API/Invoke-GitHubAPI.ps1
Write-Verbose "[$scriptName] - [public/API/Invoke-GitHubAPI.ps1] - Importing"

function Invoke-GitHubAPI {
    <#
    .SYNOPSIS
    Calls the GitHub API using the provided parameters.

    .DESCRIPTION
    This function is a wrapper around Invoke-RestMethod tailored for calling GitHub's API.
    It automatically handles the endpoint URI construction, headers, and token authentication.

    .EXAMPLE
    Invoke-GitHubAPI -ApiEndpoint '/repos/user/repo/pulls' -Method GET

    Gets all open pull requests for the specified repository.

    .EXAMPLE
    Invoke-GitHubAPI -ApiEndpoint '/repos/user/repo/pulls' -Method GET -Body @{ state = 'open' }

    Gets all open pull requests for the specified repository, filtered by the 'state' parameter.

    .EXAMPLE
    Invoke-GitHubAPI -ApiEndpoint '/repos/user/repo/pulls' -Method GET -Body @{ state = 'open' } -Accept 'application/vnd.github.v3+json'

    Gets all open pull requests for the specified repository, filtered by the 'state' parameter, and using the specified 'Accept' header.
#>

    [CmdletBinding()]
    param (
        # The HTTP method to be used for the API request. It can be one of the following: GET, POST, PUT, DELETE, or PATCH.
        [Parameter()]
        [Microsoft.PowerShell.Commands.WebRequestMethod] $Method = 'GET',

        # The base URI for the GitHub API. This is usually 'https://api.github.com', but can be adjusted if necessary.
        [Parameter()]
        [string] $ApiBaseUri = (Get-GitHubConfig -Name ApiBaseUri),

        # The specific endpoint for the API call, e.g., '/repos/user/repo/pulls'.
        [Parameter(Mandatory)]
        [string] $ApiEndpoint,

        # The body of the API request. This can be a hashtable or a string. If a hashtable is provided, it will be converted to JSON.
        [Parameter()]
        [Object] $Body,

        # The 'Accept' header for the API request. If not provided, the default will be used by GitHub's API.
        [Parameter()]
        [string] $Accept = 'application/vnd.github+json',

        # Specifies the HTTP version used for the request.
        [Parameter()]
        $HttpVersion = '2.0',

        # Support Pagination Relation Links per RFC5988.
        [Parameter()]
        $FollowRelLink = $true,

        # The secure token used for authentication in the GitHub API. It should be stored as a SecureString to ensure it's kept safe in memory.
        [Parameter()]
        [SecureString] $AccessToken = (Get-GitHubConfig -Name AccessToken),

        # The 'Content-Type' header for the API request. The default is 'application/vnd.github+json'.
        [Parameter()]
        [string] $ContentType = 'application/vnd.github+json',

        # The GitHub API version to be used. By default, it pulls from a configuration script variable.
        [Parameter()]
        [string] $Version = (Get-GitHubConfig -Name ApiVersion)
    )

    $functionName = $MyInvocation.MyCommand.Name

    $headers = @{
        Accept                 = $Accept
        'X-GitHub-Api-Version' = $Version
    }

    Remove-HashTableEntries -Hashtable $headers -NullOrEmptyValues

    $URI = ("$ApiBaseUri/" -replace '/$', '') + ("/$ApiEndpoint" -replace '^/', '')

    $APICall = @{
        Uri                     = $URI
        Method                  = $Method
        Headers                 = $Headers
        Authentication          = 'Bearer'
        Token                   = $AccessToken
        ContentType             = $ContentType
        HttpVersion             = $HttpVersion
        FollowRelLink           = $FollowRelLink
        StatusCodeVariable      = 'StatusCode'
        ResponseHeadersVariable = 'ResponseHeaders'
    }
    Remove-HashTableEntries -Hashtable $APICall -NullOrEmptyValues

    if ($Body) {
        if ($Body -is [string]) {
            $APICall.Body = $Body
        } else {
            $APICall.Body = $Body | ConvertTo-Json -Depth 100
        }
    }

    try {
        Invoke-RestMethod @APICall | Write-Output
        Write-Verbose ($StatusCode | ConvertTo-Json -Depth 100)
        Write-Verbose ($ResponseHeaders | ConvertTo-Json -Depth 100)
    } catch [System.Net.WebException] {
        Write-Error "[$functionName] - WebException - $($_.Exception.Message)"
        throw $_
    } catch {
        Write-Error "[$functionName] - GeneralException - $($_.Exception.Message)"
        throw $_
    }
}

Write-Verbose "[$scriptName] - [public/API/Invoke-GitHubAPI.ps1] - Done"
#endregion - From public/API/Invoke-GitHubAPI.ps1

Write-Verbose "[$scriptName] - [public/API] - Done"
#endregion - From public/API

#region - From public/Auth
Write-Verbose "[$scriptName] - [public/Auth] - Processing folder"

#region - From public/Auth/Connect-GitHubAccount.ps1
Write-Verbose "[$scriptName] - [public/Auth/Connect-GitHubAccount.ps1] - Importing"

function Connect-GitHubAccount {
    <#
        .SYNOPSIS
        Connects to GitHub using a personal access token or device code login.

        .DESCRIPTION
        Connects to GitHub using a personal access token or device code login.

        For device flow / device code login:
        PowerShell requests device and user verification codes and gets the authorization URL where you will enter the user verification code.
        In GitHub you will be asked to enter a user verification code at https://github.com/login/device.
        PowerShell will keep polling GitHub for the user authentication status. Once you have authorized the device,
        the app will be able to make API calls with a new access token.

        .EXAMPLE
        Connect-GitHubAccount

        Connects to GitHub using a device flow login.
        If the user has already logged in, the access token will be refreshed.

        .EXAMPLE
        Connect-GitHubAccount -AccessToken
        ! Enter your personal access token: *************

        User gets prompted for the access token and stores it in the secret store.
        The token is used when connecting to GitHub.

        .EXAMPLE
        Connect-GitHubAccount -Mode 'OAuthApp' -Scope 'gist read:org repo workflow'

        Connects to GitHub using a device flow login and sets the scope of the access token.

        .NOTES
        https://docs.github.com/en/rest/overview/other-authentication-methods#authenticating-for-saml-sso
    #>

    [Alias('Connect-GHAccount')]
    [Alias('Connect-GitHub')]
    [Alias('Connect-GH')]
    [Alias('Login-GitHubAccount')]
    [Alias('Login-GHAccount')]
    [Alias('Login-GitHub')]
    [Alias('Login-GH')]
    [OutputType([void])]
    [CmdletBinding(DefaultParameterSetName = 'DeviceFlow')]
    param (
        # Choose between authentication methods, either OAuthApp or GitHubApp.
        # For more info about the types of authentication visit:
        # https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/differences-between-github-apps-and-oauth-apps
        [Parameter(ParameterSetName = 'DeviceFlow')]
        [ValidateSet('OAuthApp', 'GitHubApp')]
        [string] $Mode = 'GitHubApp',

        # The scope of the access token, when using OAuth authentication.
        # Provide the list of scopes as space-separated values.
        # For more information on scopes visit:
        # https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps
        [Parameter(ParameterSetName = 'DeviceFlow')]
        [string] $Scope = 'gist read:org repo workflow',

        # The personal access token to use for authentication.
        [Parameter(
            Mandatory,
            ParameterSetName = 'PAT'
        )]
        [switch] $AccessToken
    )

    $envVar = Get-ChildItem -Path 'Env:' | Where-Object Name -In 'GH_TOKEN', 'GITHUB_TOKEN' | Select-Object -First 1
    $envVarPresent = $envVar.count -gt 0
    $AuthType = $envVarPresent ? 'sPAT' : $PSCmdlet.ParameterSetName

    switch ($AuthType) {
        'DeviceFlow' {
            Write-Verbose 'Logging in using device flow...'
            $clientID = $script:Auth.$Mode.ClientID
            if ($Mode -ne (Get-GitHubConfig -Name DeviceFlowType -ErrorAction SilentlyContinue)) {
                Write-Verbose "Using $Mode authentication..."
                $tokenResponse = Invoke-GitHubDeviceFlowLogin -ClientID $clientID -Scope $Scope
            } else {
                $accessTokenValidity = [datetime](Get-GitHubConfig -Name 'AccessTokenExpirationDate') - (Get-Date)
                $accessTokenIsValid = $accessTokenValidity.Seconds -gt 0
                $accessTokenValidityText = "$($accessTokenValidity.Hours):$($accessTokenValidity.Minutes):$($accessTokenValidity.Seconds)"
                if ($accessTokenIsValid) {
                    if ($accessTokenValidity -gt 4) {
                        Write-Host '✓ ' -ForegroundColor Green -NoNewline
                        Write-Host "Access token is still valid for $accessTokenValidityText ..."
                        return
                    } else {
                        Write-Host 'âš  ' -ForegroundColor Yellow -NoNewline
                        Write-Host "Access token remaining validity $accessTokenValidityText. Refreshing access token..."
                        $tokenResponse = Invoke-GitHubDeviceFlowLogin -ClientID $clientID -RefreshToken (Get-GitHubConfig -Name RefreshToken)
                    }
                } else {
                    $refreshTokenValidity = [datetime](Get-GitHubConfig -Name 'RefreshTokenExpirationDate') - (Get-Date)
                    $refreshTokenIsValid = $refreshTokenValidity.Seconds -gt 0
                    if ($refreshTokenIsValid) {
                        Write-Host 'âš  ' -ForegroundColor Yellow -NoNewline
                        Write-Verbose 'Access token expired. Refreshing access token...'
                        $tokenResponse = Invoke-GitHubDeviceFlowLogin -ClientID $clientID -RefreshToken (Get-GitHubConfig -Name RefreshToken)
                    } else {
                        Write-Verbose "Using $Mode authentication..."
                        $tokenResponse = Invoke-GitHubDeviceFlowLogin -ClientID $clientID -Scope $Scope
                    }
                }
            }
            Reset-GitHubConfig -Scope 'Auth'
            switch ($Mode) {
                'GitHubApp' {
                    $settings = @{
                        AccessToken                = ConvertTo-SecureString -AsPlainText $tokenResponse.access_token
                        AccessTokenExpirationDate  = (Get-Date).AddSeconds($tokenResponse.expires_in)
                        AccessTokenType            = $tokenResponse.access_token -replace '_.*$', '_*'
                        ApiBaseUri                 = 'https://api.github.com'
                        ApiVersion                 = '2022-11-28'
                        AuthType                   = $AuthType
                        DeviceFlowType             = $Mode
                        RefreshToken               = ConvertTo-SecureString -AsPlainText $tokenResponse.refresh_token
                        RefreshTokenExpirationDate = (Get-Date).AddSeconds($tokenResponse.refresh_token_expires_in)
                        Scope                      = $tokenResponse.scope
                    }
                }
                'OAuthApp' {
                    $settings = @{
                        AccessToken     = ConvertTo-SecureString -AsPlainText $tokenResponse.access_token
                        AccessTokenType = $tokenResponse.access_token -replace '_.*$', '_*'
                        ApiBaseUri      = 'https://api.github.com'
                        ApiVersion      = '2022-11-28'
                        AuthType        = $AuthType
                        DeviceFlowType  = $Mode
                        Scope           = $tokenResponse.scope
                    }
                }
            }
            Set-GitHubConfig @settings
            break
        }
        'PAT' {
            Write-Verbose 'Logging in using personal access token...'
            Reset-GitHubConfig -Scope 'Auth'
            Write-Host '! ' -ForegroundColor DarkYellow -NoNewline
            Start-Process 'https://github.com/settings/tokens'
            $accessTokenValue = Read-Host -Prompt 'Enter your personal access token' -AsSecureString
            $accessTokenType = (ConvertFrom-SecureString $accessTokenValue -AsPlainText) -replace '_.*$', '_*'
            if ($accessTokenType -notmatch '^ghp_|^github_pat_') {
                Write-Host 'âš  ' -ForegroundColor Yellow -NoNewline
                Write-Host "Unexpected access token format: $accessTokenType"
            }
            $settings = @{
                AccessToken     = $accessTokenValue
                AccessTokenType = $accessTokenType
                ApiBaseUri      = 'https://api.github.com'
                ApiVersion      = '2022-11-28'
                AuthType        = $AuthType
            }
            Set-GitHubConfig @settings
            break
        }
        'sPAT' {
            Write-Verbose 'Logging in using system access token...'
            Reset-GitHubConfig -Scope 'Auth'
            $prefix = $envVar.Value -replace '_.*$', '_*'
            $settings = @{
                AccessToken     = ConvertTo-SecureString -AsPlainText $envVar.Value
                AccessTokenType = $prefix
                ApiBaseUri      = 'https://api.github.com'
                ApiVersion      = '2022-11-28'
                AuthType        = 'sPAT'
            }
            Set-GitHubConfig @settings
        }
    }

    Write-Host '✓ ' -ForegroundColor Green -NoNewline
    Write-Host 'Logged in to GitHub!'
}

Write-Verbose "[$scriptName] - [public/Auth/Connect-GitHubAccount.ps1] - Done"
#endregion - From public/Auth/Connect-GitHubAccount.ps1
#region - From public/Auth/Disconnect-GitHubAccount.ps1
Write-Verbose "[$scriptName] - [public/Auth/Disconnect-GitHubAccount.ps1] - Importing"

function Disconnect-GitHubAccount {
    <#
        .SYNOPSIS
        Disconnects from GitHub and removes the current GitHub configuration.

        .DESCRIPTION
        Disconnects from GitHub and removes the current GitHub configuration.

        .EXAMPLE
        Disconnect-GitHubAccount

        Disconnects from GitHub and removes the current GitHub configuration.
    #>

    [Alias('Disconnect-GHAccount')]
    [Alias('Disconnect-GitHub')]
    [Alias('Disconnect-GH')]
    [Alias('Logout-GitHubAccount')]
    [Alias('Logout-GHAccount')]
    [Alias('Logout-GitHub')]
    [Alias('Logout-GH')]
    [Alias('Logoff-GitHubAccount')]
    [Alias('Logoff-GHAccount')]
    [Alias('Logoff-GitHub')]
    [Alias('Logoff-GH')]
    [OutputType([void])]
    [CmdletBinding()]
    param ()

    Reset-GitHubConfig -Scope 'All'

    Write-Host '✓ ' -ForegroundColor Green -NoNewline
    Write-Host 'Logged out of GitHub!'
}

Write-Verbose "[$scriptName] - [public/Auth/Disconnect-GitHubAccount.ps1] - Done"
#endregion - From public/Auth/Disconnect-GitHubAccount.ps1

Write-Verbose "[$scriptName] - [public/Auth] - Done"
#endregion - From public/Auth

#region - From public/Config
Write-Verbose "[$scriptName] - [public/Config] - Processing folder"

#region - From public/Config/Get-GitHubConfig.ps1
Write-Verbose "[$scriptName] - [public/Config/Get-GitHubConfig.ps1] - Importing"

function Get-GitHubConfig {
    <#
        .SYNOPSIS
        Get configuration value.

        .DESCRIPTION
        Get a named configuration value from the GitHub configuration file.

        .EXAMPLE
        Get-GitHubConfig -Name ApiBaseUri

        Get the current GitHub configuration for the ApiBaseUri.
    #>

    [Alias('Get-GHConfig')]
    [Alias('GGHC')]
    [OutputType([object])]
    [CmdletBinding()]
    param (
        # Choose a configuration name to get.
        [Parameter()]
        [string] $Name
    )

    $prefix = $script:SecretVault.Prefix

    switch($Name) {
        'AccessToken' {
            Get-Secret -Name "$prefix`AccessToken"
        }
        'RefreshToken' {
            Get-Secret -Name "$prefix`RefreshToken"
        }
        'RefreshTokenExpirationDate' {
            $RefreshTokenData = Get-SecretInfo -Name "$prefix`RefreshToken"
            $RefreshTokenData.Metadata | ConvertFrom-HashTable | ConvertTo-HashTable | Select-Object -ExpandProperty $Name
        }
        default {
            $AccessTokenData = Get-SecretInfo -Name "$prefix`AccessToken"
            $AccessTokenData.Metadata | ConvertFrom-HashTable | ConvertTo-HashTable | Select-Object -ExpandProperty $Name
        }
    }
}

Write-Verbose "[$scriptName] - [public/Config/Get-GitHubConfig.ps1] - Done"
#endregion - From public/Config/Get-GitHubConfig.ps1
#region - From public/Config/Set-GitHubConfig.ps1
Write-Verbose "[$scriptName] - [public/Config/Set-GitHubConfig.ps1] - Importing"

function Set-GitHubConfig {
    <#
        .SYNOPSIS
        Set the GitHub configuration.

        .DESCRIPTION
        Set the GitHub configuration. Specific scopes can be set by using the parameters.

        .EXAMPLE
        Set-GitHubConfig -APIBaseURI 'https://api.github.com" -APIVersion '2022-11-28'

        Sets the App.API scope of the GitHub configuration.

        .EXAMPLE
        Set-GitHubConfig -Name "MyFavouriteRepo" -Value 'https://github.com/PSModule/GitHub'

        Sets a item called 'MyFavouriteRepo' in the GitHub configuration.
    #>

    [Alias('Set-GHConfig')]
    [CmdletBinding()]
    param (
        # Set the access token type.
        [Parameter()]
        [string] $AccessTokenType,

        # Set the access token.
        [Parameter()]
        [securestring] $AccessToken,

        # Set the access token expiration date.
        [Parameter()]
        [datetime] $AccessTokenExpirationDate,

        # Set the API Base URI.
        [Parameter()]
        [string] $ApiBaseUri,

        # Set the GitHub API Version.
        [Parameter()]
        [string] $ApiVersion,

        # Set the authentication type.
        [Parameter()]
        [string] $AuthType,

        # Set the device flow type.
        [Parameter()]
        [string] $DeviceFlowType,

        # Set the default for the Owner parameter.
        [Parameter()]
        [string] $Owner,

        # Set the refresh token.
        [Parameter()]
        [securestring] $RefreshToken,

        # Set the refresh token expiration date.
        [Parameter()]
        [datetime] $RefreshTokenExpirationDate,

        # Set the default for the Repo parameter.
        [Parameter()]
        [string] $Repo,

        # Set the scope.
        [Parameter()]
        [string] $Scope,

        # Set the GitHub username.
        [Parameter()]
        [string] $UserName
    )

    $prefix = $script:SecretVault.Prefix

    #region AccessToken
    $secretName = "$prefix`AccessToken"
    $removeKeys = 'AccessToken', 'RefreshToken', 'RefreshTokenExpirationDate'
    $keepTypes = 'String', 'Int', 'DateTime'

    # Get existing metadata if it exists
    $newSecretMetadata = @{}
    if (Get-SecretInfo -Name $secretName) {
        $secretGetInfoParam = @{
            Name  = $secretName
            Vault = $script:SecretVault.Name
        }
        $secretInfo = Get-SecretInfo @secretGetInfoParam
        Write-Verbose "$secretName - secretInfo : $($secretInfo | Out-String)"
        $secretMetadata = $secretInfo.Metadata | ConvertFrom-HashTable | ConvertTo-HashTable
        $newSecretMetadata = Join-Hashtable -Main $newSecretMetadata -Overrides $secretMetadata
    }

    # Get metadata updates from parameters and clean up unwanted data
    $updateSecretMetadata = $PSBoundParameters | ConvertFrom-HashTable | ConvertTo-HashTable
    Write-Verbose "updateSecretMetadata : $($updateSecretMetadata | Out-String)"
    Write-Verbose "updateSecretMetadataType : $($updateSecretMetadata.GetType())"
    Remove-HashTableEntries -Hashtable $updateSecretMetadata -KeepTypes $keepTypes -RemoveNames $removeKeys
    Write-Verbose "updateSecretMetadata : $($updateSecretMetadata | Out-String)"

    $newSecretMetadata = Join-HashTable -Main $newSecretMetadata -Overrides $updateSecretMetadata
    Write-Verbose "newSecretMetadata : $($newSecretMetadata | Out-String)"
    Write-Verbose "newSecretMetadataType : $($newSecretMetadata.GetType())"

    if ($AccessToken) {
        $accessTokenSetParam = @{
            Name               = $secretName
            Vault              = $script:SecretVault.Name
            SecureStringSecret = $AccessToken
        }
        Set-Secret @accessTokenSetParam
    }

    if (Get-SecretInfo -Name $secretName) {
        $secretSetInfoParam = @{
            Name     = $secretName
            Vault    = $script:SecretVault.Name
            Metadata = $newSecretMetadata
        }
        Set-SecretInfo @secretSetInfoParam
    }
    #endregion AccessToken

    #region RefreshToken
    $secretName = "$prefix`RefreshToken"
    $removeKeys = 'AccessToken', 'RefreshToken', 'AccessTokenExpirationDate'

    # Get existing metadata if it exists
    $newSecretMetadata = @{}
    if (Get-SecretInfo -Name $secretName) {
        $secretGetInfoParam = @{
            Name  = $secretName
            Vault = $script:SecretVault.Name
        }
        $secretInfo = Get-SecretInfo @secretGetInfoParam
        Write-Verbose "$secretName - secretInfo : $($secretInfo | Out-String)"
        $secretMetadata = $secretInfo.Metadata | ConvertFrom-HashTable | ConvertTo-HashTable
        $newSecretMetadata = Join-Hashtable -Main $newSecretMetadata -Overrides $secretMetadata
    }

    # Get metadata updates from parameters and clean up unwanted data
    $updateSecretMetadata = $PSBoundParameters | ConvertFrom-HashTable | ConvertTo-HashTable
    Write-Verbose "updateSecretMetadata : $($updateSecretMetadata | Out-String)"
    Write-Verbose "updateSecretMetadataType : $($updateSecretMetadata.GetType())"
    Remove-HashTableEntries -Hashtable $updateSecretMetadata -KeepTypes $keepTypes -RemoveNames $removeKeys
    Write-Verbose "updateSecretMetadata : $($updateSecretMetadata | Out-String)"

    $newSecretMetadata = Join-HashTable -Main $newSecretMetadata -Overrides $updateSecretMetadata
    Write-Verbose "newSecretMetadata : $($newSecretMetadata | Out-String)"
    Write-Verbose "newSecretMetadataType : $($newSecretMetadata.GetType())"

    if ($RefreshToken) {
        $refreshTokenSetParam = @{
            Name               = $secretName
            Vault              = $script:SecretVault.Name
            SecureStringSecret = $RefreshToken
        }
        Set-Secret @refreshTokenSetParam
    }

    if (Get-SecretInfo -Name $secretName) {
        $secretSetInfoParam = @{
            Name     = $secretName
            Vault    = $script:SecretVault.Name
            Metadata = $newSecretMetadata
        }
        Set-SecretInfo @secretSetInfoParam -Verbose
    }
    #endregion AccessToken
}

Write-Verbose "[$scriptName] - [public/Config/Set-GitHubConfig.ps1] - Done"
#endregion - From public/Config/Set-GitHubConfig.ps1

Write-Verbose "[$scriptName] - [public/Config] - Done"
#endregion - From public/Config

#region - From public/Meta
Write-Verbose "[$scriptName] - [public/Meta] - Processing folder"

#region - From public/Meta/Get-GitHubApiVersions.ps1
Write-Verbose "[$scriptName] - [public/Meta/Get-GitHubApiVersions.ps1] - Importing"

function Get-GitHubApiVersions {
    <#
        .SYNOPSIS
        Get all API versions.

        .DESCRIPTION
        Get all supported GitHub API versions.

        .EXAMPLE
        Get-GitHubApiVersions

        Get all supported GitHub API versions.

        .NOTES
        https://docs.github.com/rest/meta/meta#get-all-api-versions
    #>

    [OutputType([string[]])]
    [CmdletBinding()]
    param ()

    $inputObject = @{
        ApiEndpoint = '/versions'
        Method      = 'GET'
    }

    $response = Invoke-GitHubAPI @inputObject

    $response
}

Write-Verbose "[$scriptName] - [public/Meta/Get-GitHubApiVersions.ps1] - Done"
#endregion - From public/Meta/Get-GitHubApiVersions.ps1
#region - From public/Meta/Get-GitHubMeta.ps1
Write-Verbose "[$scriptName] - [public/Meta/Get-GitHubMeta.ps1] - Importing"

function Get-GitHubMeta {
    <#
        .SYNOPSIS
        Get GitHub meta information.

        .DESCRIPTION
        Returns meta information about GitHub, including a list of GitHub's IP addresses. For more information, see "[About GitHub's IP addresses](https://docs.github.com/articles/about-github-s-ip-addresses/)."

        The API's response also includes a list of GitHub's domain names.

        The values shown in the documentation's response are example values. You must always query the API directly to get the latest values.

        **Note:** This endpoint returns both IPv4 and IPv6 addresses. However, not all features support IPv6. You should refer to the specific documentation for each feature to determine if IPv6 is supported.

        .EXAMPLE
        Get-GitHubMeta

        Returns meta information about GitHub, including a list of GitHub's IP addresses.

        .NOTES
        https://docs.github.com/rest/meta/meta#get-apiname-meta-information
    #>

    [OutputType([object])]
    [CmdletBinding()]
    param ()

    $inputObject = @{
        ApiEndpoint = '/meta'
        Method      = 'GET'
    }

    $response = Invoke-GitHubAPI @inputObject

    $response
}

Write-Verbose "[$scriptName] - [public/Meta/Get-GitHubMeta.ps1] - Done"
#endregion - From public/Meta/Get-GitHubMeta.ps1
#region - From public/Meta/Get-GitHubOctocat.ps1
Write-Verbose "[$scriptName] - [public/Meta/Get-GitHubOctocat.ps1] - Importing"

function Get-GitHubOctocat {
    <#
        .SYNOPSIS
        Get Octocat.

        .DESCRIPTION
        Get the octocat as ASCII art.

        .EXAMPLE
        Get-GitHubOctocat

        Get the octocat as ASCII art

        .EXAMPLE
        Get-GitHubOctocat -S 'The glass is never half empty. It's just twice as big as it needs to be.'

        Get the octocat as ASCII art with a custom saying

        .NOTES
        https://docs.github.com/rest/meta/meta#get-octocat
    #>

    [OutputType([string])]
    [CmdletBinding()]
    param (
        # The words to show in Octocat's speech bubble
        [Parameter()]
        [Alias('Say')]
        [Alias('Saying')]
        [string]
        $S = 'The glass is never half empty. Its just twice as big as it needs to be.'
    )

    # $query = [System.Web.HttpUtility]::UrlEncode($S)
    # $query = [System.Uri]::EscapeDataString($S)

    $body = @{
        s = $S
    }

    $inputObject = @{
        APIEndpoint = '/octocat'
        Method      = 'GET'
        Body        = $body
    }

    $response = Invoke-GitHubAPI @inputObject

    $response
}

Write-Verbose "[$scriptName] - [public/Meta/Get-GitHubOctocat.ps1] - Done"
#endregion - From public/Meta/Get-GitHubOctocat.ps1
#region - From public/Meta/Get-GitHubRoot.ps1
Write-Verbose "[$scriptName] - [public/Meta/Get-GitHubRoot.ps1] - Importing"

function Get-GitHubRoot {
    <#
        .SYNOPSIS
        GitHub API Root.

        .DESCRIPTION
        Get Hypermedia links to resources accessible in GitHub's REST API.

        .EXAMPLE
        Get-GitHubRoot

        Get the root endpoint for the GitHub API.

        .NOTES
        https://docs.github.com/rest/meta/meta#github-api-root
    #>

    [CmdletBinding()]
    param ()

    $inputObject = @{
        APIEndpoint = '/'
        Method      = 'GET'
    }

    $response = Invoke-GitHubAPI @inputObject

    $response
}

Write-Verbose "[$scriptName] - [public/Meta/Get-GitHubRoot.ps1] - Done"
#endregion - From public/Meta/Get-GitHubRoot.ps1
#region - From public/Meta/Get-GitHubZen.ps1
Write-Verbose "[$scriptName] - [public/Meta/Get-GitHubZen.ps1] - Importing"

function Get-GitHubZen {
    <#
    .SYNOPSIS
    Get the Zen of GitHub.

    .DESCRIPTION
    Get a random sentence from the Zen of GitHub.

    .EXAMPLE
    Get-GitHubZen

    Get a random sentence from the Zen of GitHub.

    .NOTES
    https://docs.github.com/rest/meta/meta#get-the-zen-of-github
    #>

    [CmdletBinding()]
    param ()

    $inputObject = @{
        APIEndpoint = '/zen'
        Method      = 'GET'
    }

    $response = Invoke-GitHubAPI @inputObject

    $response
}

Write-Verbose "[$scriptName] - [public/Meta/Get-GitHubZen.ps1] - Done"
#endregion - From public/Meta/Get-GitHubZen.ps1

Write-Verbose "[$scriptName] - [public/Meta] - Done"
#endregion - From public/Meta

#region - From public/Users
Write-Verbose "[$scriptName] - [public/Users] - Processing folder"

#region - From public/Users/Get-GitHubUser.ps1
Write-Verbose "[$scriptName] - [public/Users/Get-GitHubUser.ps1] - Importing"

function Get-GitHubUser {
    <#
        .SYNOPSIS
        Get the authenticated user

        .DESCRIPTION
        If the authenticated user is authenticated with an OAuth token with the `user` scope, then the response lists public and private profile information.
        If the authenticated user is authenticated through OAuth without the `user` scope, then the response lists only public profile information.

        .EXAMPLE
        Get-GitHubUser

        Get the authenticated user

        .NOTES
        https://docs.github.com/rest/users/users#get-the-authenticated-user
    #>

    [OutputType([pscustomobject])]
    [Alias('Get-GitHubContext')]
    [CmdletBinding()]
    param ()

    $inputObject = @{
        APIEndpoint = '/user'
        Method      = 'GET'
    }

    $response = Invoke-GitHubAPI @inputObject

    $response
}

Write-Verbose "[$scriptName] - [public/Users/Get-GitHubUser.ps1] - Done"
#endregion - From public/Users/Get-GitHubUser.ps1
#region - From public/Users/Set-GitHubUser.ps1
Write-Verbose "[$scriptName] - [public/Users/Set-GitHubUser.ps1] - Importing"

function Set-GitHubUser {
    <#
        .SYNOPSIS
        Update the authenticated user

        .DESCRIPTION
        **Note:** If your email is set to private and you send an `email` parameter as part of this request
        to update your profile, your privacy settings are still enforced: the email address will not be
        displayed on your public profile or via the API.

        .EXAMPLE
        Set-GitHubUser -Name 'octocat'

        Update the authenticated user's name to 'octocat'

        .EXAMPLE
        Set-GitHubUser -Location 'San Francisco'

        Update the authenticated user's location to 'San Francisco'

        .EXAMPLE
        Set-GitHubUser -Hireable $true -Bio 'I love programming'

        Update the authenticated user's hiring availability to 'true' and their biography to 'I love programming'

        .NOTES
        https://docs.github.com/rest/users/users#update-the-authenticated-user
    #>

    [OutputType([void])]
    [Alias('Update-GitHubUser')]
    [CmdletBinding()]
    param (
        # The new name of the user.
        [Parameter()]
        [string] $Name,

        # The publicly visible email address of the user.
        [Parameter()]
        [string] $Email,

        # The new blog URL of the user.
        [Parameter()]
        [string] $Blog,

        # The new Twitter username of the user.
        [Parameter()]
        [string] $TwitterUsername,

        # The new company of the user.
        [Parameter()]
        [string] $Company,

        # The new location of the user.
        [Parameter()]
        [string] $Location,

        # The new hiring availability of the user.
        [Parameter()]
        [boolean] $Hireable,

        # The new short biography of the user.
        [Parameter()]
        [string] $Bio
    )

    $body = @{}

    $PSBoundParameters.GetEnumerator() | ForEach-Object {
        $body.($_.Key) = $_.Value
    }

    $inputObject = @{
        APIEndpoint = '/user'
        Body        = $body
        Method      = 'PATCH'
    }

    $inputObject

    $response = Invoke-GitHubAPI @inputObject -Verbose

    $response
}

Write-Verbose "[$scriptName] - [public/Users/Set-GitHubUser.ps1] - Done"
#endregion - From public/Users/Set-GitHubUser.ps1

Write-Verbose "[$scriptName] - [public/Users] - Done"
#endregion - From public/Users


Write-Verbose "[$scriptName] - [public] - Done"
#endregion - From public

#region - From GitHub.ps1
Write-Verbose "[$scriptName] - [GitHub.ps1] - Importing"

$scriptFilePath = $MyInvocation.MyCommand.Path

Write-Verbose "[$scriptFilePath] - Initializing GitHub module..." -Verbose

Initialize-SecretVault -Name $script:SecretVault.Name -Type $script:SecretVault.Type

# Autologon if a token is present in environment variables
$envVar = Get-ChildItem -Path 'Env:' | Where-Object Name -In 'GH_TOKEN', 'GITHUB_TOKEN' | Select-Object -First 1
$envVarPresent = $envVar.count -gt 0
if ($envVarPresent) {
    Connect-GitHubAccount
}
Write-Verbose "[$scriptName] - [GitHub.ps1] - Done"
#endregion - From GitHub.ps1

Export-ModuleMember -Function 'Invoke-GitHubAPI','Connect-GitHubAccount','Disconnect-GitHubAccount','Get-GitHubConfig','Set-GitHubConfig','Get-GitHubApiVersions','Get-GitHubMeta','Get-GitHubOctocat','Get-GitHubRoot','Get-GitHubZen','Get-GitHubUser','Set-GitHubUser' -Cmdlet '' -Variable '' -Alias '*'