Support/Package/Execution/Eigenverft.Manifested.Sandbox.Package.Npm.ps1

<#
    Eigenverft.Manifested.Sandbox.Package.Npm
#>


function Get-PackageNpmGlobalConfigPath {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [psobject]$PackageResult
    )

    $packageRoot = Get-PackageRootFromInventoryPath -PackageAssignmentInventoryFilePath ([string]$PackageResult.PackageConfig.PackageAssignmentInventoryFilePath)
    return ([System.IO.Path]::GetFullPath((Join-Path $packageRoot 'Configuration\External\npm\npmrc')))
}

function New-PackageNpmCacheDirectory {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [psobject]$PackageResult
    )

    $packageRoot = Get-PackageRootFromInventoryPath -PackageAssignmentInventoryFilePath ([string]$PackageResult.PackageConfig.PackageAssignmentInventoryFilePath)
    $segments = @(
        'Caches'
        'npm'
        [string]$PackageResult.DefinitionId
        [string]$PackageResult.Package.releaseTrack
        [string]$PackageResult.Package.version
        [string]$PackageResult.Package.artifactDistributionVariant
    ) | ForEach-Object {
        ([string]$_).Trim() -replace '[\\/:\*\?"<>\|]', '-'
    }

    $cacheDirectory = [System.IO.Path]::GetFullPath((Join-Path $packageRoot ($segments -join '\')))
    $null = New-Item -ItemType Directory -Path $cacheDirectory -Force
    return $cacheDirectory
}

function Initialize-PackageNpmGlobalConfig {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$GlobalConfigPath
    )

    $resolvedPath = [System.IO.Path]::GetFullPath($GlobalConfigPath)
    $directoryPath = Split-Path -Parent $resolvedPath
    if (-not [string]::IsNullOrWhiteSpace($directoryPath)) {
        $null = New-Item -ItemType Directory -Path $directoryPath -Force
    }

    if (-not (Test-Path -LiteralPath $resolvedPath -PathType Leaf)) {
        Set-Content -LiteralPath $resolvedPath -Value '' -Encoding UTF8
    }

    return $resolvedPath
}

function Resolve-PackageNpmInstallerCommand {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [psobject]$PackageResult
    )

    $install = Get-PackageAssignedInstallOperation -Release $PackageResult.Package
    if (-not $install) {
        throw "Package npm global package install for '$($PackageResult.PackageId)' requires packageOperations.assigned.install on the selected release."
    }
    if (-not $install.PSObject.Properties['installerCommand'] -or [string]::IsNullOrWhiteSpace([string]$install.installerCommand)) {
        throw "Package npm global package install for '$($PackageResult.PackageId)' requires packageOperations.assigned.install.installerCommand."
    }

    $installerCommand = [string]$install.installerCommand
    $dependencyInfo = Resolve-PackageDependencyCommandPath -PackageResult $PackageResult -CommandName $installerCommand
    Write-PackageExecutionMessage -Message ("[STATE] Installer command ready: definition='{0}', command='{1}', path='{2}'." -f $dependencyInfo.DefinitionId, $dependencyInfo.Command, $dependencyInfo.CommandPath)

    return $dependencyInfo
}

function Resolve-PackageNpmNodeCommandPath {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [psobject]$PackageResult,

        [Parameter(Mandatory = $true)]
        [string]$NpmCommandPath
    )

    $npmDirectory = Split-Path -Parent ([System.IO.Path]::GetFullPath($NpmCommandPath))
    foreach ($candidateName in @('node.exe', 'node.cmd', 'node.bat')) {
        $candidatePath = Join-Path $npmDirectory $candidateName
        if (Test-Path -LiteralPath $candidatePath -PathType Leaf) {
            return ([System.IO.Path]::GetFullPath($candidatePath))
        }
    }

    try {
        $dependencyInfo = Resolve-PackageDependencyCommandPath -PackageResult $PackageResult -CommandName 'node'
        if ($dependencyInfo -and -not [string]::IsNullOrWhiteSpace([string]$dependencyInfo.CommandPath)) {
            return ([System.IO.Path]::GetFullPath([string]$dependencyInfo.CommandPath))
        }
    }
    catch {
        # Fall through to PATH lookup; npm materialization still reports a clear error below.
    }

    $pathCommand = Get-Command -Name 'node.exe' -ErrorAction SilentlyContinue
    if ($pathCommand -and -not [string]::IsNullOrWhiteSpace([string]$pathCommand.Source)) {
        return ([System.IO.Path]::GetFullPath([string]$pathCommand.Source))
    }

    throw "Package npm materialization for '$($PackageResult.PackageId)' requires node.exe to parse npm lock metadata."
}

function Test-PackageNpmMaterializedInstallKind {
    [CmdletBinding()]
    param(
        [AllowNull()]
        [psobject]$Package
    )

    $install = Get-PackageAssignedInstallOperation -Release $Package
    return ($install -and
        $install.PSObject.Properties['kind'] -and
        [string]::Equals([string]$install.kind, 'npmMaterializedInstallGlobalPackage', [System.StringComparison]::OrdinalIgnoreCase))
}

function Get-PackageNpmResolvedPackageSpec {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [psobject]$PackageResult
    )

    $install = Get-PackageAssignedInstallOperation -Release $PackageResult.Package
    if (-not $install -or -not $install.PSObject.Properties['packageSpec'] -or [string]::IsNullOrWhiteSpace([string]$install.packageSpec)) {
        throw "Package npm materialized install for '$($PackageResult.PackageId)' requires packageOperations.assigned.install.packageSpec."
    }

    return (Resolve-PackageTemplateText -Text ([string]$install.packageSpec) -PackageConfig $PackageResult.PackageConfig -Package $PackageResult.Package)
}

function Get-PackageNpmMaterializationDirectory {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [psobject]$PackageResult
    )

    if ([string]::IsNullOrWhiteSpace([string]$PackageResult.PackageFileStagingDirectory)) {
        throw "Package npm materialization for '$($PackageResult.PackageId)' requires a package file staging directory."
    }

    return ([System.IO.Path]::GetFullPath((Join-Path ([string]$PackageResult.PackageFileStagingDirectory) 'npm-materialized')))
}

function Get-PackageNpmMaterializationManifestPath {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Directory
    )

    return ([System.IO.Path]::GetFullPath((Join-Path $Directory 'npm-materialization.json')))
}

function Get-PackageNpmPlatform {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [psobject]$PackageConfig
    )

    switch -Regex ([string]$PackageConfig.Platform) {
        '^(windows|win32)$' { return 'win32' }
        '^(macos|darwin)$' { return 'darwin' }
        '^linux$' { return 'linux' }
        default { return ([string]$PackageConfig.Platform).ToLowerInvariant() }
    }
}

function Get-PackageNpmArchitecture {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [psobject]$PackageConfig
    )

    switch -Regex ([string]$PackageConfig.Architecture) {
        '^(x64|amd64)$' { return 'x64' }
        '^(arm64|aarch64)$' { return 'arm64' }
        '^(x86|ia32)$' { return 'ia32' }
        default { return ([string]$PackageConfig.Architecture).ToLowerInvariant() }
    }
}

function Test-PackageNpmIntegrity {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Path,

        [Parameter(Mandatory = $true)]
        [string]$Integrity
    )

    if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) {
        return $false
    }

    foreach ($token in @(([string]$Integrity) -split '\s+')) {
        if ([string]::IsNullOrWhiteSpace($token) -or $token -notmatch '^(?<algorithm>sha1|sha256|sha384|sha512)-(?<value>.+)$') {
            continue
        }

        $algorithm = $Matches.algorithm.ToUpperInvariant()
        $expected = $Matches.value
        $hashAlgorithm = [System.Security.Cryptography.HashAlgorithm]::Create($algorithm)
        if (-not $hashAlgorithm) {
            continue
        }

        $stream = [System.IO.File]::OpenRead([System.IO.Path]::GetFullPath($Path))
        try {
            $actual = [Convert]::ToBase64String($hashAlgorithm.ComputeHash($stream))
        }
        finally {
            $stream.Dispose()
            $hashAlgorithm.Dispose()
        }

        if ([string]::Equals($actual, $expected, [System.StringComparison]::Ordinal)) {
            return $true
        }
    }

    return $false
}

function Test-PackageNpmMaterializationDirectory {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Directory,

        [Parameter(Mandatory = $true)]
        [string]$PackageSpec,

        [Parameter(Mandatory = $true)]
        [string]$NpmPlatform,

        [Parameter(Mandatory = $true)]
        [string]$NpmArchitecture
    )

    $manifestPath = Get-PackageNpmMaterializationManifestPath -Directory $Directory
    if (-not (Test-Path -LiteralPath $manifestPath -PathType Leaf)) {
        return $null
    }

    try {
        $manifest = Get-Content -LiteralPath $manifestPath -Raw | ConvertFrom-Json
    }
    catch {
        return $null
    }

    if (-not $manifest -or
        -not [string]::Equals([string]$manifest.packageSpec, $PackageSpec, [System.StringComparison]::OrdinalIgnoreCase) -or
        -not [string]::Equals([string]$manifest.npmPlatform, $NpmPlatform, [System.StringComparison]::OrdinalIgnoreCase) -or
        -not [string]::Equals([string]$manifest.npmArchitecture, $NpmArchitecture, [System.StringComparison]::OrdinalIgnoreCase)) {
        return $null
    }

    $tarballPaths = New-Object System.Collections.Generic.List[string]
    foreach ($package in @($manifest.packages)) {
        if (-not $package.PSObject.Properties['fileName'] -or
            [string]::IsNullOrWhiteSpace([string]$package.fileName) -or
            -not $package.PSObject.Properties['integrity'] -or
            [string]::IsNullOrWhiteSpace([string]$package.integrity)) {
            return $null
        }

        $tarballPath = [System.IO.Path]::GetFullPath((Join-Path $Directory ([string]$package.fileName)))
        if (-not (Test-PackageNpmIntegrity -Path $tarballPath -Integrity ([string]$package.integrity))) {
            return $null
        }
        $tarballPaths.Add($tarballPath) | Out-Null
    }

    if ($tarballPaths.Count -eq 0) {
        return $null
    }

    return [pscustomobject]@{
        ManifestPath = [System.IO.Path]::GetFullPath($manifestPath)
        Manifest     = $manifest
        TarballPaths = @($tarballPaths.ToArray())
    }
}

function Copy-PackageNpmMaterializationDirectory {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$SourceDirectory,

        [Parameter(Mandatory = $true)]
        [string]$TargetDirectory,

        [Parameter(Mandatory = $true)]
        [psobject]$Materialization
    )

    $null = New-Item -ItemType Directory -Path $TargetDirectory -Force
    $sourceManifestPath = Get-PackageNpmMaterializationManifestPath -Directory $SourceDirectory
    $targetManifestPath = Get-PackageNpmMaterializationManifestPath -Directory $TargetDirectory
    $null = Copy-FileToPath -SourcePath $sourceManifestPath -TargetPath $targetManifestPath -Overwrite

    $copiedTarballs = New-Object System.Collections.Generic.List[string]
    foreach ($package in @($Materialization.Manifest.packages)) {
        $sourcePath = [System.IO.Path]::GetFullPath((Join-Path $SourceDirectory ([string]$package.fileName)))
        $targetPath = [System.IO.Path]::GetFullPath((Join-Path $TargetDirectory ([string]$package.fileName)))
        $null = Copy-FileToPath -SourcePath $sourcePath -TargetPath $targetPath -Overwrite
        $copiedTarballs.Add($targetPath) | Out-Null
    }

    return [pscustomobject]@{
        ManifestPath = $targetManifestPath
        Manifest     = $Materialization.Manifest
        TarballPaths = @($copiedTarballs.ToArray())
    }
}

function Find-PackageNpmMaterializationInDepots {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [psobject]$PackageResult,

        [Parameter(Mandatory = $true)]
        [string]$PackageSpec,

        [Parameter(Mandatory = $true)]
        [string]$NpmPlatform,

        [Parameter(Mandatory = $true)]
        [string]$NpmArchitecture
    )

    foreach ($depotSource in @(Get-PackagePackageDepotSources -PackageConfig $PackageResult.PackageConfig)) {
        if ([string]::IsNullOrWhiteSpace([string]$depotSource.basePath)) {
            continue
        }

        $candidateDirectory = [System.IO.Path]::GetFullPath((Join-Path ([string]$depotSource.basePath) ([string]$PackageResult.PackageDepotRelativeDirectory)))
        $materialization = Test-PackageNpmMaterializationDirectory -Directory $candidateDirectory -PackageSpec $PackageSpec -NpmPlatform $NpmPlatform -NpmArchitecture $NpmArchitecture
        if ($materialization) {
            $materialization | Add-Member -MemberType NoteProperty -Name SourceId -Value ([string]$depotSource.id) -Force
            $materialization | Add-Member -MemberType NoteProperty -Name SourceDirectory -Value $candidateDirectory -Force
            return $materialization
        }
    }

    return $null
}

function New-PackageNpmMaterializationManifest {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [psobject]$PackageResult,

        [Parameter(Mandatory = $true)]
        [string]$PackageSpec,

        [Parameter(Mandatory = $true)]
        [string]$NpmPlatform,

        [Parameter(Mandatory = $true)]
        [string]$NpmArchitecture,

        [Parameter(Mandatory = $true)]
        [object[]]$Packages
    )

    return [pscustomobject]@{
        schemaVersion    = '1.0'
        packageSpec      = $PackageSpec
        definitionId     = [string]$PackageResult.DefinitionId
        packageId        = [string]$PackageResult.PackageId
        packageVersion   = [string]$PackageResult.Package.version
        releaseTrack     = [string]$PackageResult.Package.releaseTrack
        artifactDistributionVariant = [string]$PackageResult.Package.artifactDistributionVariant
        npmPlatform      = $NpmPlatform
        npmArchitecture  = $NpmArchitecture
        generatedAtUtc   = [DateTime]::UtcNow.ToString('o')
        packages         = @($Packages)
    }
}

function Write-PackageNpmMaterializationHelperScript {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$HelperScriptPath
    )

    $script = @'
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const childProcess = require('child_process');
 
const requestPath = process.argv[2];
const resultPath = process.argv[3];
 
function writeJson(filePath, value) {
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
  fs.writeFileSync(filePath, JSON.stringify(value, null, 2), 'utf8');
}
 
function readJson(filePath) {
  const text = fs.readFileSync(filePath, 'utf8').replace(/^\uFEFF/, '');
  return JSON.parse(text);
}
 
function trimOutput(value) {
  return String(value || '').trim().slice(0, 4000);
}
 
function run(command, args, cwd, operation) {
  const result = childProcess.spawnSync(command, args, {
    cwd,
    shell: true,
    encoding: 'utf8',
    windowsHide: true
  });
  if (result.error) {
    throw new Error(`${operation} failed to start: ${result.error.message}`);
  }
  const status = typeof result.status === 'number' ? result.status : 0;
  if (status !== 0) {
    throw new Error(`${operation} failed with exit code ${status}. stdout=${trimOutput(result.stdout)} stderr=${trimOutput(result.stderr)}`);
  }
  return result.stdout || '';
}
 
function parseJsonOutput(output, operation) {
  const text = String(output || '').trim();
  if (!text) {
    throw new Error(`${operation} did not return JSON output.`);
  }
  try {
    const parsed = JSON.parse(text);
    return Array.isArray(parsed) ? parsed : [parsed];
  } catch (_) {
    const start = text.indexOf('[');
    const end = text.lastIndexOf(']');
    if (start >= 0 && end > start) {
      const parsed = JSON.parse(text.substring(start, end + 1));
      return Array.isArray(parsed) ? parsed : [parsed];
    }
    throw new Error(`${operation} did not return parseable JSON output.`);
  }
}
 
function allowed(list, value) {
  if (!Array.isArray(list) || list.length === 0) return true;
  const positives = list.filter((entry) => !String(entry).startsWith('!')).map(String);
  const negatives = list.filter((entry) => String(entry).startsWith('!')).map((entry) => String(entry).substring(1));
  if (negatives.includes(value)) return false;
  if (positives.length > 0 && !positives.includes(value)) return false;
  return true;
}
 
function packageName(lockKey) {
  const pieces = String(lockKey).split('node_modules/');
  return pieces[pieces.length - 1].replace(/\/$/, '');
}
 
function fileNameFromResolved(resolved) {
  try {
    const url = new URL(resolved);
    const parts = url.pathname.split('/');
    return decodeURIComponent(parts[parts.length - 1]);
  } catch (_) {
    return '';
  }
}
 
function selectedPackages(lock, npmPlatform, npmArchitecture) {
  const packages = [];
  for (const [lockKey, entry] of Object.entries(lock.packages || {})) {
    if (!String(lockKey).includes('node_modules/')) continue;
    if (!entry || !entry.version || !entry.resolved || !entry.integrity) continue;
    if (!allowed(entry.os, npmPlatform) || !allowed(entry.cpu, npmArchitecture)) continue;
    packages.push({
      name: packageName(lockKey),
      version: String(entry.version),
      resolved: String(entry.resolved),
      integrity: String(entry.integrity),
      fileName: fileNameFromResolved(String(entry.resolved)),
      optional: !!entry.optional,
      os: Array.isArray(entry.os) ? entry.os.map(String) : [],
      cpu: Array.isArray(entry.cpu) ? entry.cpu.map(String) : []
    });
  }
  return packages;
}
 
function verifyIntegrity(filePath, integrity) {
  if (!filePath || !integrity || !fs.existsSync(filePath)) return false;
  const content = fs.readFileSync(filePath);
  for (const token of String(integrity).split(/\s+/)) {
    const match = /^([a-z0-9]+)-(.+)$/i.exec(token);
    if (!match) continue;
    try {
      const actual = crypto.createHash(match[1].toLowerCase()).update(content).digest('base64');
      if (actual === match[2]) return true;
    } catch (_) {
      continue;
    }
  }
  return false;
}
 
function globalConfigArgs(globalConfigPath) {
  return globalConfigPath ? ['--globalconfig', globalConfigPath] : [];
}
 
function materializePackage(request, packageInfo) {
  if (packageInfo.fileName && packageInfo.integrity) {
    const existingPath = path.resolve(request.targetDirectory, packageInfo.fileName);
    if (verifyIntegrity(existingPath, packageInfo.integrity)) {
      return packageInfo;
    }
  }
 
  const packageSpec = `${packageInfo.name}@${packageInfo.version}`;
  const packOutput = run(
    request.npmCommandPath,
    ['pack', packageSpec, '--pack-destination', request.targetDirectory, '--json', '--cache', request.cacheDirectory, ...globalConfigArgs(request.globalConfigPath)],
    request.targetDirectory,
    `npm pack for '${packageSpec}'`
  );
  const packItems = parseJsonOutput(packOutput, `npm pack for '${packageSpec}'`);
  const packItem = packItems.find((item) => String(item.name).toLowerCase() === String(packageInfo.name).toLowerCase() && String(item.version) === String(packageInfo.version)) || packItems[0];
  const fileName = String(packItem.filename || packageInfo.fileName || '');
  const integrity = String(packItem.integrity || packageInfo.integrity || '');
  if (!fileName) throw new Error(`npm pack for '${packageSpec}' did not report a tarball filename.`);
  if (!integrity) throw new Error(`npm pack for '${packageSpec}' did not report integrity metadata.`);
 
  const targetPath = path.resolve(request.targetDirectory, fileName);
  if (!verifyIntegrity(targetPath, integrity)) {
    throw new Error(`npm pack output '${fileName}' did not satisfy integrity metadata.`);
  }
 
  return {
    name: String(packageInfo.name),
    version: String(packageInfo.version),
    resolved: String(packageInfo.resolved || ''),
    integrity,
    fileName,
    optional: !!packageInfo.optional,
    os: Array.isArray(packageInfo.os) ? packageInfo.os.map(String) : [],
    cpu: Array.isArray(packageInfo.cpu) ? packageInfo.cpu.map(String) : []
  };
}
 
function main() {
  const request = readJson(requestPath);
  fs.rmSync(request.resolutionDirectory, { recursive: true, force: true });
  fs.mkdirSync(request.resolutionDirectory, { recursive: true });
  fs.mkdirSync(request.targetDirectory, { recursive: true });
 
  run(
    request.npmCommandPath,
    ['install', '--package-lock-only', '--ignore-scripts', '--no-audit', '--no-fund', '--cache', request.cacheDirectory, ...globalConfigArgs(request.globalConfigPath), request.packageSpec],
    request.resolutionDirectory,
    `npm metadata resolution for '${request.packageSpec}'`
  );
 
  const lockFilePath = path.resolve(request.resolutionDirectory, 'package-lock.json');
  if (!fs.existsSync(lockFilePath)) {
    throw new Error(`npm metadata resolution for '${request.packageSpec}' did not produce package-lock.json.`);
  }
 
  const lock = readJson(lockFilePath);
  const packages = selectedPackages(lock, request.npmPlatform, request.npmArchitecture);
  if (packages.length === 0) {
    throw new Error(`npm metadata resolution for '${request.packageSpec}' did not produce any materializable packages.`);
  }
 
  const materializedPackages = packages.map((packageInfo) => materializePackage(request, packageInfo));
  const manifest = {
    schemaVersion: '1.0',
    packageSpec: request.packageSpec,
    definitionId: request.definitionId,
    packageId: request.packageId,
    packageVersion: request.packageVersion,
    releaseTrack: request.releaseTrack,
    artifactDistributionVariant: request.artifactDistributionVariant,
    npmPlatform: request.npmPlatform,
    npmArchitecture: request.npmArchitecture,
    generatedAtUtc: new Date().toISOString(),
    packages: materializedPackages
  };
 
  writeJson(request.manifestPath, manifest);
  writeJson(resultPath, {
    success: true,
    status: 'Materialized',
    packageCount: materializedPackages.length,
    manifestPath: request.manifestPath,
    packageNames: materializedPackages.map((item) => item.name)
  });
}
 
try {
  main();
} catch (error) {
  writeJson(resultPath, {
    success: false,
    status: 'Failed',
    errorMessage: error && error.message ? error.message : String(error),
    stack: error && error.stack ? error.stack : ''
  });
  process.exit(1);
}
'@


    $resolvedPath = [System.IO.Path]::GetFullPath($HelperScriptPath)
    $directoryPath = Split-Path -Parent $resolvedPath
    if (-not [string]::IsNullOrWhiteSpace($directoryPath)) {
        $null = New-Item -ItemType Directory -Path $directoryPath -Force
    }
    Set-Content -LiteralPath $resolvedPath -Value $script -Encoding UTF8
    return $resolvedPath
}

function New-PackageNpmMaterializationFromRegistry {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [psobject]$PackageResult,

        [Parameter(Mandatory = $true)]
        [string]$PackageSpec,

        [Parameter(Mandatory = $true)]
        [string]$NpmPlatform,

        [Parameter(Mandatory = $true)]
        [string]$NpmArchitecture,

        [Parameter(Mandatory = $true)]
        [string]$TargetDirectory
    )

    $installerCommandInfo = Resolve-PackageNpmInstallerCommand -PackageResult $PackageResult
    $nodeCommandPath = Resolve-PackageNpmNodeCommandPath -PackageResult $PackageResult -NpmCommandPath ([string]$installerCommandInfo.CommandPath)
    $cacheDirectory = New-PackageNpmCacheDirectory -PackageResult $PackageResult
    $globalConfigPath = Initialize-PackageNpmGlobalConfig -GlobalConfigPath (Get-PackageNpmGlobalConfigPath -PackageResult $PackageResult)

    Write-PackageExecutionMessage -Message ("[STATE] Materializing npm package spec '{0}'." -f $PackageSpec)
    $targetFullPath = [System.IO.Path]::GetFullPath($TargetDirectory)
    $resolutionDirectory = [System.IO.Path]::GetFullPath((Join-Path $targetFullPath '.npm-resolution'))
    $helperScriptPath = [System.IO.Path]::GetFullPath((Join-Path $targetFullPath 'materialize-npm-package.js'))
    $requestPath = [System.IO.Path]::GetFullPath((Join-Path $targetFullPath 'npm-materialization-request.json'))
    $resultPath = [System.IO.Path]::GetFullPath((Join-Path $targetFullPath 'npm-materialization-result.json'))
    $manifestPath = Get-PackageNpmMaterializationManifestPath -Directory $targetFullPath

    $null = New-Item -ItemType Directory -Path $targetFullPath -Force
    $helperScriptPath = Write-PackageNpmMaterializationHelperScript -HelperScriptPath $helperScriptPath
    $request = [pscustomobject]@{
        packageSpec                 = $PackageSpec
        definitionId                = [string]$PackageResult.DefinitionId
        packageId                   = [string]$PackageResult.PackageId
        packageVersion              = [string]$PackageResult.Package.version
        releaseTrack                = [string]$PackageResult.Package.releaseTrack
        artifactDistributionVariant = [string]$PackageResult.Package.artifactDistributionVariant
        npmPlatform                 = $NpmPlatform
        npmArchitecture             = $NpmArchitecture
        npmCommandPath              = [System.IO.Path]::GetFullPath([string]$installerCommandInfo.CommandPath)
        cacheDirectory              = [System.IO.Path]::GetFullPath($cacheDirectory)
        globalConfigPath            = [System.IO.Path]::GetFullPath($globalConfigPath)
        targetDirectory             = $targetFullPath
        resolutionDirectory         = $resolutionDirectory
        manifestPath                = $manifestPath
    }
    $request | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $requestPath -Encoding UTF8

    Write-PackageExecutionMessage -Message ("[PATH] npm materialization helper: {0}" -f $helperScriptPath)
    Write-PackageExecutionMessage -Message ("[PATH] npm materialization request: {0}" -f $requestPath)
    Write-PackageExecutionMessage -Message ("[PATH] npm materialization result: {0}" -f $resultPath)

    & $nodeCommandPath $helperScriptPath $requestPath $resultPath
    $exitCode = $LASTEXITCODE
    if ($null -eq $exitCode) {
        $exitCode = 0
    }
    $helperResult = $null
    if (Test-Path -LiteralPath $resultPath -PathType Leaf) {
        try {
            $helperResult = Get-Content -LiteralPath $resultPath -Raw | ConvertFrom-Json
        }
        catch {
            $helperResult = $null
        }
    }
    if ($exitCode -ne 0 -or ($helperResult -and $helperResult.PSObject.Properties['success'] -and -not [bool]$helperResult.success)) {
        $errorMessage = if ($helperResult -and $helperResult.PSObject.Properties['errorMessage'] -and -not [string]::IsNullOrWhiteSpace([string]$helperResult.errorMessage)) {
            [string]$helperResult.errorMessage
        }
        else {
            "npm materialization helper failed with exit code $exitCode."
        }
        throw $errorMessage
    }

    $materialization = Test-PackageNpmMaterializationDirectory -Directory $targetFullPath -PackageSpec $PackageSpec -NpmPlatform $NpmPlatform -NpmArchitecture $NpmArchitecture
    if (-not $materialization) {
        throw "npm materialization for '$PackageSpec' could not be validated after download."
    }

    return $materialization
}

function Invoke-PackageNpmMaterializationDepotDistribution {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [psobject]$PackageResult,

        [Parameter(Mandatory = $true)]
        [psobject]$Materialization
    )

    $mode = if ($PackageResult.PackageConfig.PSObject.Properties['DepotDistributionMode'] -and
        -not [string]::IsNullOrWhiteSpace([string]$PackageResult.PackageConfig.DepotDistributionMode)) {
        [string]$PackageResult.PackageConfig.DepotDistributionMode
    }
    else {
        'packageFocused'
    }

    $files = New-Object System.Collections.Generic.List[object]
    $files.Add([pscustomobject]@{ FileName = 'npm-materialization.json'; SourcePath = [string]$Materialization.ManifestPath }) | Out-Null
    foreach ($package in @($Materialization.Manifest.packages)) {
        $sourcePath = [System.IO.Path]::GetFullPath((Join-Path (Split-Path -Parent ([string]$Materialization.ManifestPath)) ([string]$package.fileName)))
        $files.Add([pscustomobject]@{ FileName = [string]$package.fileName; SourcePath = $sourcePath }) | Out-Null
    }

    $actions = New-Object System.Collections.Generic.List[object]
    if ([string]::Equals($mode, 'disabled', [System.StringComparison]::OrdinalIgnoreCase)) {
        return [pscustomobject]@{ Mode = $mode; Status = 'Skipped'; Reason = 'DisabledByPolicy'; Actions = @(); CopiedCount = 0; FailedCount = 0; SkippedCount = 0 }
    }

    foreach ($mirrorSource in @(Get-PackageDepotDistributionTargets -PackageConfig $PackageResult.PackageConfig)) {
        foreach ($file in @($files.ToArray())) {
            if (-not [string]::Equals([string]$mirrorSource.kind, 'filesystem', [System.StringComparison]::OrdinalIgnoreCase)) {
                $actions.Add([pscustomobject]@{ DepotId = [string]$mirrorSource.id; FileName = [string]$file.FileName; Action = 'Skip'; Status = 'Skipped'; Reason = 'UnsupportedDepotKind'; SourcePath = [string]$file.SourcePath; TargetPath = $null; EnsureExists = [bool]$mirrorSource.ensureExists; ErrorMessage = $null }) | Out-Null
                continue
            }
            if ([string]::IsNullOrWhiteSpace([string]$mirrorSource.basePath)) {
                $actions.Add([pscustomobject]@{ DepotId = [string]$mirrorSource.id; FileName = [string]$file.FileName; Action = 'Skip'; Status = 'Skipped'; Reason = 'MissingBasePath'; SourcePath = [string]$file.SourcePath; TargetPath = $null; EnsureExists = [bool]$mirrorSource.ensureExists; ErrorMessage = $null }) | Out-Null
                continue
            }

            $targetDirectory = [System.IO.Path]::GetFullPath((Join-Path ([string]$mirrorSource.basePath) ([string]$PackageResult.PackageDepotRelativeDirectory)))
            $targetPath = [System.IO.Path]::GetFullPath((Join-Path $targetDirectory ([string]$file.FileName)))
            $sourceFullPath = [System.IO.Path]::GetFullPath([string]$file.SourcePath)
            if ([string]::Equals($sourceFullPath, $targetPath, [System.StringComparison]::OrdinalIgnoreCase)) {
                $actions.Add([pscustomobject]@{ DepotId = [string]$mirrorSource.id; FileName = [string]$file.FileName; Action = 'Skip'; Status = 'Skipped'; Reason = 'SourceIsTarget'; SourcePath = $sourceFullPath; TargetPath = $targetPath; EnsureExists = [bool]$mirrorSource.ensureExists; ErrorMessage = $null }) | Out-Null
                continue
            }

            $match = Test-PackageDepotDistributionFileMatches -SourcePath $sourceFullPath -TargetPath $targetPath
            if ($match.Matches) {
                $actions.Add([pscustomobject]@{ DepotId = [string]$mirrorSource.id; FileName = [string]$file.FileName; Action = 'Skip'; Status = 'Skipped'; Reason = [string]$match.Reason; SourcePath = $sourceFullPath; TargetPath = $targetPath; EnsureExists = [bool]$mirrorSource.ensureExists; ErrorMessage = $null }) | Out-Null
                continue
            }
            if ([string]::Equals($mode, 'packageFocused', [System.StringComparison]::OrdinalIgnoreCase) -and
                -not [string]::Equals([string]$match.Reason, 'Missing', [System.StringComparison]::OrdinalIgnoreCase)) {
                $actions.Add([pscustomobject]@{ DepotId = [string]$mirrorSource.id; FileName = [string]$file.FileName; Action = 'Skip'; Status = 'Skipped'; Reason = 'DifferentTargetPreservedByPackageFocusedPolicy'; SourcePath = $sourceFullPath; TargetPath = $targetPath; EnsureExists = [bool]$mirrorSource.ensureExists; ErrorMessage = [string]$match.Reason }) | Out-Null
                continue
            }

            try {
                if ($mirrorSource.ensureExists) {
                    $null = New-Item -ItemType Directory -Path $targetDirectory -Force
                }
                $null = Copy-FileToPath -SourcePath $sourceFullPath -TargetPath $targetPath -Overwrite
                $actions.Add([pscustomobject]@{ DepotId = [string]$mirrorSource.id; FileName = [string]$file.FileName; Action = 'Copy'; Status = 'Copied'; Reason = [string]$match.Reason; SourcePath = $sourceFullPath; TargetPath = $targetPath; EnsureExists = [bool]$mirrorSource.ensureExists; ErrorMessage = $null }) | Out-Null
            }
            catch {
                $actions.Add([pscustomobject]@{ DepotId = [string]$mirrorSource.id; FileName = [string]$file.FileName; Action = 'Copy'; Status = 'Failed'; Reason = [string]$match.Reason; SourcePath = $sourceFullPath; TargetPath = $targetPath; EnsureExists = [bool]$mirrorSource.ensureExists; ErrorMessage = $_.Exception.Message }) | Out-Null
            }
        }
    }

    $copiedCount = @($actions.ToArray() | Where-Object { [string]::Equals([string]$_.Status, 'Copied', [System.StringComparison]::OrdinalIgnoreCase) }).Count
    $failedCount = @($actions.ToArray() | Where-Object { [string]::Equals([string]$_.Status, 'Failed', [System.StringComparison]::OrdinalIgnoreCase) }).Count
    $skippedCount = @($actions.ToArray() | Where-Object { [string]::Equals([string]$_.Status, 'Skipped', [System.StringComparison]::OrdinalIgnoreCase) }).Count

    return [pscustomobject]@{
        Mode         = $mode
        Status       = if ($actions.Count -eq 0) { 'Skipped' } else { 'Planned' }
        Reason       = if ($actions.Count -eq 0) { 'NoDepotTargets' } else { $null }
        Actions      = @($actions.ToArray())
        CopiedCount  = $copiedCount
        FailedCount  = $failedCount
        SkippedCount = $skippedCount
    }
}

function Invoke-PackageNpmMaterialization {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [psobject]$PackageResult
    )

    if (-not (Test-PackageNpmMaterializedInstallKind -Package $PackageResult.Package)) {
        return $PackageResult
    }

    $packageSpec = Get-PackageNpmResolvedPackageSpec -PackageResult $PackageResult
    $npmPlatform = Get-PackageNpmPlatform -PackageConfig $PackageResult.PackageConfig
    $npmArchitecture = Get-PackageNpmArchitecture -PackageConfig $PackageResult.PackageConfig
    $stageDirectory = Get-PackageNpmMaterializationDirectory -PackageResult $PackageResult
    Remove-PathIfExists -Path $stageDirectory | Out-Null

    $depotMaterialization = Find-PackageNpmMaterializationInDepots -PackageResult $PackageResult -PackageSpec $packageSpec -NpmPlatform $npmPlatform -NpmArchitecture $npmArchitecture
    if ($depotMaterialization) {
        $copied = Copy-PackageNpmMaterializationDirectory -SourceDirectory ([string]$depotMaterialization.SourceDirectory) -TargetDirectory $stageDirectory -Materialization $depotMaterialization
        $PackageResult | Add-Member -MemberType NoteProperty -Name NpmMaterialization -Value ([pscustomobject]@{
            Success         = $true
            Status          = 'HydratedFromDepot'
            PackageSpec     = $packageSpec
            NpmPlatform     = $npmPlatform
            NpmArchitecture = $npmArchitecture
            SourceId        = [string]$depotMaterialization.SourceId
            ManifestPath    = [string]$copied.ManifestPath
            Manifest        = $copied.Manifest
            TarballPaths    = @($copied.TarballPaths)
            DepotDistribution = $null
        }) -Force
        Write-PackageExecutionMessage -Message ("[ACTION] Hydrated npm materialization from depot '{0}'." -f [string]$depotMaterialization.SourceId)
    }
    else {
        $materialization = New-PackageNpmMaterializationFromRegistry -PackageResult $PackageResult -PackageSpec $packageSpec -NpmPlatform $npmPlatform -NpmArchitecture $npmArchitecture -TargetDirectory $stageDirectory
        $PackageResult | Add-Member -MemberType NoteProperty -Name NpmMaterialization -Value ([pscustomobject]@{
            Success         = $true
            Status          = 'MaterializedFromRegistry'
            PackageSpec     = $packageSpec
            NpmPlatform     = $npmPlatform
            NpmArchitecture = $npmArchitecture
            SourceId        = 'npmRegistry'
            ManifestPath    = [string]$materialization.ManifestPath
            Manifest        = $materialization.Manifest
            TarballPaths    = @($materialization.TarballPaths)
            DepotDistribution = $null
        }) -Force
        Write-PackageExecutionMessage -Message ("[ACTION] Materialized npm package spec '{0}' with {1} tarball(s)." -f $packageSpec, @($materialization.TarballPaths).Count)
    }

    $distribution = Invoke-PackageNpmMaterializationDepotDistribution -PackageResult $PackageResult -Materialization $PackageResult.NpmMaterialization
    $PackageResult.NpmMaterialization.DepotDistribution = $distribution
    Write-PackageExecutionMessage -Message ("[STATE] npm materialization depot distribution completed: mode='{0}', copied={1}, skipped={2}, failed={3}." -f [string]$distribution.Mode, [int]$distribution.CopiedCount, [int]$distribution.SkippedCount, [int]$distribution.FailedCount)

    return $PackageResult
}

function Install-PackageNpmPackage {
<#
.SYNOPSIS
Installs an exact npm package spec into a staged Package-owned prefix.
#>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [psobject]$PackageResult
    )

    $install = Get-PackageAssignedInstallOperation -Release $PackageResult.Package
    if (-not $install) {
        throw "Package npm global package install for '$($PackageResult.PackageId)' requires packageOperations.assigned.install on the selected release."
    }
    if (-not $install.PSObject.Properties['packageSpec'] -or [string]::IsNullOrWhiteSpace([string]$install.packageSpec)) {
        throw "Package npm global package install for '$($PackageResult.PackageId)' requires packageOperations.assigned.install.packageSpec."
    }

    $packageSpec = Resolve-PackageTemplateText -Text ([string]$install.packageSpec) -PackageConfig $PackageResult.PackageConfig -Package $PackageResult.Package
    $installerCommandInfo = Resolve-PackageNpmInstallerCommand -PackageResult $PackageResult
    $cacheDirectory = New-PackageNpmCacheDirectory -PackageResult $PackageResult
    $globalConfigPath = Initialize-PackageNpmGlobalConfig -GlobalConfigPath (Get-PackageNpmGlobalConfigPath -PackageResult $PackageResult)
    if ([string]::IsNullOrWhiteSpace([string]$PackageResult.PackageInstallStageDirectory)) {
        throw "Package npm global package install for '$($PackageResult.PackageId)' requires a package install stage directory."
    }
    $stagePath = [System.IO.Path]::GetFullPath([string]$PackageResult.PackageInstallStageDirectory)
    Remove-PathIfExists -Path $stagePath | Out-Null
    $null = New-Item -ItemType Directory -Path $stagePath -Force
    $stagePromoted = $false

    $commandArguments = @('install', '-g', '--prefix', $stagePath, '--cache', $cacheDirectory)
    $commandArguments += @(Get-NpmGlobalConfigArguments -GlobalConfigPath $globalConfigPath)
    $commandArguments += $packageSpec

    Write-PackageExecutionMessage -Message ("[STATE] npm global package install:")
    Write-PackageExecutionMessage -Message ("[PATH] npm command: {0}" -f $installerCommandInfo.CommandPath)
    Write-PackageExecutionMessage -Message ("[PATH] npm stage: {0}" -f $stagePath)
    Write-PackageExecutionMessage -Message ("[PATH] npm cache: {0}" -f $cacheDirectory)
    Write-PackageExecutionMessage -Message ("[PATH] npm global config: {0}" -f $globalConfigPath)
    Write-PackageExecutionMessage -Message ("[STATE] npm package spec: {0}" -f $packageSpec)

    try {
        Push-Location $stagePath
        try {
            & $installerCommandInfo.CommandPath @commandArguments
            $exitCode = $LASTEXITCODE
            if ($null -eq $exitCode) {
                $exitCode = 0
            }
        }
        finally {
            Pop-Location
        }

        if ($exitCode -ne 0) {
            throw "Package npm global package install for '$($PackageResult.PackageId)' failed with exit code $exitCode."
        }

        $installParent = Split-Path -Parent $PackageResult.InstallDirectory
        if (-not [string]::IsNullOrWhiteSpace($installParent)) {
            $null = New-Item -ItemType Directory -Path $installParent -Force
        }
        Remove-PathIfExists -Path $PackageResult.InstallDirectory | Out-Null
        Move-Item -LiteralPath $stagePath -Destination $PackageResult.InstallDirectory -Force
        $stagePromoted = $true
    }
    finally {
        if (-not $stagePromoted) {
            Write-PackageExecutionMessage -Level 'WRN' -Message ("[WARN] Preserving failed npm package install stage '{0}' for inspection." -f $stagePath)
        }
    }

    return [pscustomobject]@{
        Status           = Get-PackageOwnedInstallStatus -PackageResult $PackageResult
        InstallKind      = 'npmGlobalPackage'
        InstallDirectory = $PackageResult.InstallDirectory
        ReusedExisting   = $false
        InstallerCommand = $installerCommandInfo.Command
        InstallerCommandPath = $installerCommandInfo.CommandPath
        PackageSpec      = $packageSpec
        CommandArguments = @($commandArguments)
        CacheDirectory   = $cacheDirectory
        GlobalConfigPath = $globalConfigPath
        StagePath        = $stagePath
        ExitCode         = $exitCode
    }
}

function Install-PackageNpmMaterializedInstallGlobalPackage {
<#
.SYNOPSIS
Installs a materialized npm package spec from local tarballs into a staged Package-owned prefix.
#>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [psobject]$PackageResult
    )

    $install = Get-PackageAssignedInstallOperation -Release $PackageResult.Package
    if (-not $install) {
        throw "Package npm materialized install for '$($PackageResult.PackageId)' requires packageOperations.assigned.install on the selected release."
    }
    if (-not $PackageResult.PSObject.Properties['NpmMaterialization'] -or -not $PackageResult.NpmMaterialization -or -not $PackageResult.NpmMaterialization.Success) {
        throw "Package npm materialized install for '$($PackageResult.PackageId)' requires prepared npm materialization."
    }

    $packageSpec = Get-PackageNpmResolvedPackageSpec -PackageResult $PackageResult
    $tarballPaths = @($PackageResult.NpmMaterialization.TarballPaths)
    if ($tarballPaths.Count -eq 0) {
        throw "Package npm materialized install for '$($PackageResult.PackageId)' has no local materialized tarballs."
    }
    foreach ($tarballPath in $tarballPaths) {
        if ([string]::IsNullOrWhiteSpace([string]$tarballPath) -or -not (Test-Path -LiteralPath ([string]$tarballPath) -PathType Leaf)) {
            throw "Package npm materialized install for '$($PackageResult.PackageId)' is missing materialized tarball '$tarballPath'."
        }
    }

    $installerCommandInfo = Resolve-PackageNpmInstallerCommand -PackageResult $PackageResult
    $cacheDirectory = New-PackageNpmCacheDirectory -PackageResult $PackageResult
    $globalConfigPath = Initialize-PackageNpmGlobalConfig -GlobalConfigPath (Get-PackageNpmGlobalConfigPath -PackageResult $PackageResult)
    if ([string]::IsNullOrWhiteSpace([string]$PackageResult.PackageInstallStageDirectory)) {
        throw "Package npm materialized install for '$($PackageResult.PackageId)' requires a package install stage directory."
    }

    $stagePath = [System.IO.Path]::GetFullPath([string]$PackageResult.PackageInstallStageDirectory)
    Remove-PathIfExists -Path $stagePath | Out-Null
    $null = New-Item -ItemType Directory -Path $stagePath -Force
    $stagePromoted = $false

    $cacheAddArguments = @('cache', 'add')
    $cacheAddArguments += @($tarballPaths)
    $cacheAddArguments += @('--cache', $cacheDirectory)
    $cacheAddArguments += @(Get-NpmGlobalConfigArguments -GlobalConfigPath $globalConfigPath)

    $commandArguments = @('install', '-g', '--prefix', $stagePath, '--cache', $cacheDirectory, '--offline')
    $commandArguments += @(Get-NpmGlobalConfigArguments -GlobalConfigPath $globalConfigPath)
    $commandArguments += $packageSpec

    Write-PackageExecutionMessage -Message ("[STATE] npm materialized package install:")
    Write-PackageExecutionMessage -Message ("[PATH] npm command: {0}" -f $installerCommandInfo.CommandPath)
    Write-PackageExecutionMessage -Message ("[PATH] npm stage: {0}" -f $stagePath)
    Write-PackageExecutionMessage -Message ("[PATH] npm cache: {0}" -f $cacheDirectory)
    Write-PackageExecutionMessage -Message ("[PATH] npm materialization manifest: {0}" -f [string]$PackageResult.NpmMaterialization.ManifestPath)
    Write-PackageExecutionMessage -Message ("[STATE] npm package spec: {0}" -f $packageSpec)

    try {
        & ([string]$installerCommandInfo.CommandPath) @cacheAddArguments
        $cacheAddExitCode = $LASTEXITCODE
        if ($null -eq $cacheAddExitCode) {
            $cacheAddExitCode = 0
        }
        if ($cacheAddExitCode -ne 0) {
            throw "npm cache add for materialized package '$($PackageResult.PackageId)' failed with exit code $cacheAddExitCode."
        }

        Push-Location $stagePath
        try {
            & ([string]$installerCommandInfo.CommandPath) @commandArguments
            $exitCode = $LASTEXITCODE
            if ($null -eq $exitCode) {
                $exitCode = 0
            }
        }
        finally {
            Pop-Location
        }

        if ($exitCode -ne 0) {
            throw "Package npm materialized install for '$($PackageResult.PackageId)' failed with exit code $exitCode."
        }

        $installParent = Split-Path -Parent $PackageResult.InstallDirectory
        if (-not [string]::IsNullOrWhiteSpace($installParent)) {
            $null = New-Item -ItemType Directory -Path $installParent -Force
        }
        Remove-PathIfExists -Path $PackageResult.InstallDirectory | Out-Null
        Move-Item -LiteralPath $stagePath -Destination $PackageResult.InstallDirectory -Force
        $stagePromoted = $true
    }
    finally {
        if (-not $stagePromoted) {
            Write-PackageExecutionMessage -Level 'WRN' -Message ("[WARN] Preserving failed npm materialized install stage '{0}' for inspection." -f $stagePath)
        }
    }

    return [pscustomobject]@{
        Status           = Get-PackageOwnedInstallStatus -PackageResult $PackageResult
        InstallKind      = 'npmMaterializedInstallGlobalPackage'
        InstallDirectory = $PackageResult.InstallDirectory
        ReusedExisting   = $false
        InstallerCommand = $installerCommandInfo.Command
        InstallerCommandPath = $installerCommandInfo.CommandPath
        PackageSpec      = $packageSpec
        MaterializationManifestPath = [string]$PackageResult.NpmMaterialization.ManifestPath
        MaterializedTarballPaths = @($tarballPaths)
        CacheAddArguments = @($cacheAddArguments)
        CommandArguments = @($commandArguments)
        CacheDirectory   = $cacheDirectory
        GlobalConfigPath = $globalConfigPath
        StagePath        = $stagePath
        ExitCode         = $exitCode
    }
}