param( [string]$DistPath = "D:\workspace\vignette\apps\web\dist", [string[]]$DeploymentBases = @( "https://ed0cddb4.vignette-b1q.pages.dev", "https://505155c4.vignette-b1q.pages.dev", "https://564dbed9.vignette-b1q.pages.dev", "https://1b3b6d5b.vignette-b1q.pages.dev", "https://0a339b77.vignette-b1q.pages.dev" ) ) $ErrorActionPreference = "Stop" [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false) $OutputEncoding = [System.Text.UTF8Encoding]::new($false) $distRoot = [IO.Path]::GetFullPath((Resolve-Path -LiteralPath $DistPath).Path) $distPrefix = $distRoot.TrimEnd([IO.Path]::DirectorySeparatorChar) + [IO.Path]::DirectorySeparatorChar $totalAdded = 0 function Resolve-DistTarget { param([string]$AssetPath) $relative = $AssetPath.TrimStart("/").Replace("/", [IO.Path]::DirectorySeparatorChar) $target = [IO.Path]::GetFullPath((Join-Path $distRoot $relative)) if (!$target.StartsWith($distPrefix, [StringComparison]::OrdinalIgnoreCase)) { throw "Asset target escaped the deployment directory: $AssetPath" } return $target } foreach ($base in $DeploymentBases) { $origin = $base.TrimEnd("/") $seen = [System.Collections.Generic.HashSet[string]]::new( [System.StringComparer]::OrdinalIgnoreCase ) $queue = [System.Collections.Generic.Queue[string]]::new() $html = ( Invoke-WebRequest ` -Uri "$origin/login?preserve=$([guid]::NewGuid().ToString('N'))" ` -UseBasicParsing ` -TimeoutSec 30 ).Content foreach ($match in [regex]::Matches( $html, '(?:src|href)="(/assets/[^\"]+\.(?:js|css))"' )) { $queue.Enqueue($match.Groups[1].Value) } $added = 0 while ($queue.Count -gt 0) { $asset = $queue.Dequeue() if (!$seen.Add($asset)) { continue } $target = Resolve-DistTarget -AssetPath $asset $parent = Split-Path -Parent $target if (!(Test-Path -LiteralPath $parent)) { New-Item -ItemType Directory -Path $parent -Force | Out-Null } $download = "$target.download" $response = Invoke-WebRequest ` -Uri "$origin$asset" ` -UseBasicParsing ` -TimeoutSec 30 ` -OutFile $download ` -PassThru $contentType = [string]$response.Headers["Content-Type"] if ($asset.EndsWith(".js") -and $contentType -notmatch "javascript") { Remove-Item -LiteralPath $download -Force throw "Expected JavaScript response: $origin$asset ($contentType)" } if ($asset.EndsWith(".css") -and $contentType -notmatch "text/css") { Remove-Item -LiteralPath $download -Force throw "Expected CSS response: $origin$asset ($contentType)" } if (Test-Path -LiteralPath $target) { Remove-Item -LiteralPath $download -Force } else { Move-Item -LiteralPath $download -Destination $target $added += 1 $totalAdded += 1 } if ($asset -match '\.(?:js|css)$') { $source = Get-Content -LiteralPath $target -Raw -Encoding UTF8 foreach ($match in [regex]::Matches( $source, '(?:(?:/assets/)|(?:\./))([A-Za-z0-9_.-]+\.(?:js|css))' )) { $queue.Enqueue("/assets/$($match.Groups[1].Value)") } } } Write-Output "Preserved $origin (graph=$($seen.Count), added=$added)" } Write-Output "Total previous deployment assets added: $totalAdded"