Private/Start-DFModulePrewarm.ps1

#Requires -Version 7.0

function Start-DFModulePrewarm {
    <#
    .SYNOPSIS
        Fires a background job that imports each named module in its own
        throwaway runspace, purely to warm OS/CLR-level caches.
    .DESCRIPTION
        PowerShell-level session state -- loaded modules, defined functions,
        $global: variables -- is not shared across runspaces (confirmed
        empirically -- see docs/superpowers/specs/2026-09-05-startup-perf-audit.md
        Part 2), so the import performed here is never visible to the
        caller's session. This function's only purpose is the side effect of
        touching the module's files once before the caller's own (unchanged)
        Import-Module call reaches them -- that later, real import is then
        fast, due to already-warm OS/CLR-level caches (measured ~77%
        reduction on a representative module, reproduced 3/3).

        That isolation is at the PowerShell session-state level only.
        Start-ThreadJob runs its scriptblock on a thread inside the SAME
        process as the caller (unlike Start-Job, which is a separate
        process), so process-global .NET/CLR static state IS shared with
        the caller. A module whose import touches shared static/global .NET
        state -- not just PowerShell session state -- is not safe to point
        at this function: importing it here, concurrently with anything
        else touching that same static state, is a real (if narrow)
        concurrency risk. PSReadLine is the concrete example that prompted
        this note -- it keeps its key-handler dispatch table on a
        process-global static singleton, and PSFzf's import touches it. Any
        `Tools/<name>.json` can opt a tool's module out of prewarming
        entirely with a top-level `"prewarm": false` field (see
        `Tools/psreadline.json`, which sets it for exactly this reason);
        `Register-DFTool` excludes such tools from the module list it
        passes here.

        Nothing depends on this job succeeding, finishing before the caller
        continues, or running at all: a module that fails to import here is
        silently ignored (the caller's own real import will report any real
        failure normally), and a caller that never waits on the returned
        job simply gets today's synchronous-import cost for whichever
        modules the job didn't reach in time -- never worse than not
        calling this function at all. If Start-ThreadJob itself is
        unavailable (e.g. Microsoft.PowerShell.ThreadJob cannot be found),
        that failure is caught and this function returns $null, same as
        the empty-input case -- never worse than not calling this function
        at all.

        Assumes the named modules have no import-time side effects beyond
        session-local state (defining functions, format/type data, etc.) --
        true of Terminal-Icons/PSFzf/posh-git, this function's motivating
        callers. A module whose import writes files, calls the network, or
        otherwise mutates state outside its own session would have that
        side effect run twice (once here, discarded; once for real) if
        pointed at this function -- not a fit for that kind of module.
    .PARAMETER ModuleNames
        Module names to pre-import, e.g. @('Terminal-Icons', 'PSFzf'). May
        be empty.
    .OUTPUTS
        [System.Management.Automation.Job] the started background job, or
        $null when -ModuleNames is empty or Start-ThreadJob itself fails.
        Never call Receive-Job on it for its result -- there is nothing
        meaningful to receive, since the import happened in a runspace the
        caller can't see into. The caller should Remove-Job -Force it once
        done with its own work, whether or not the job has finished by then.
    .EXAMPLE
        Start-DFModulePrewarm -ModuleNames @('Terminal-Icons', 'PSFzf', 'posh-git')
    #>

    [CmdletBinding()]
    [OutputType([System.Management.Automation.Job])]
    param(
        [Parameter(Mandatory)]
        [AllowEmptyCollection()]
        [string[]]$ModuleNames
    )

    if (-not $ModuleNames) {
        return $null
    }

    try {
        Start-ThreadJob -ScriptBlock {
            param([string[]]$Names)
            foreach ($name in $Names) {
                try { Import-Module -Name $name -ErrorAction Stop } catch { }
            }
        } -ArgumentList (, $ModuleNames)
    } catch {
        # Start-ThreadJob itself failed (e.g. Microsoft.PowerShell.ThreadJob is
        # unavailable) -- degrade silently, same shape as the empty-input
        # early return above. The caller's own real Import-Module downstream
        # is unaffected either way; see .DESCRIPTION.
        $null
    }
}