Snapshot.Playwright
0.1.0
dotnet add package Snapshot.Playwright --version 0.1.0
NuGet\Install-Package Snapshot.Playwright -Version 0.1.0
<PackageReference Include="Snapshot.Playwright" Version="0.1.0" />
<PackageVersion Include="Snapshot.Playwright" Version="0.1.0" />
<PackageReference Include="Snapshot.Playwright" />
paket add Snapshot.Playwright --version 0.1.0
#r "nuget: Snapshot.Playwright, 0.1.0"
#:package Snapshot.Playwright@0.1.0
#addin nuget:?package=Snapshot.Playwright&version=0.1.0
#tool nuget:?package=Snapshot.Playwright&version=0.1.0
Snapshot Protocol
Snapshot Protocol turns a fully rendered static web application into a deployable ZIP whose routes contain complete HTML, route-specific metadata, and the original client application required for hydration.
It is designed for Blazor WebAssembly, React, Angular, Vue, other SPAs, static hosts, IPFS, and any environment where SSR is unavailable or undesirable but crawlers still need complete HTML.
The protocol does not guess when a page is ready. Your application declares readiness only after its final asynchronous state has rendered.
What it produces
The archive root is the deployment root:
index.html
index/
index.html
snapshot-manifest.json
sitemap.xml
_framework/
css/
products/
one/
index.html
There is no enclosing folder inside the ZIP. You can upload it directly where supported, extract it into a web root, stream the ZIP as one object, or stream its entries one-by-one into systems such as IPFS.
The root route is the one special case: the original /index.html SPA/WASM loader is always preserved, while the rendered / snapshot is stored at /index/index.html. Root gateway generation is enabled by default and can be disabled with --no-root-gateway; disabling it never permits the loader to be overwritten.
Quick start
Add the browser client to your application loader. Sites that deliberately want crawlers to discover the optional root gateway may also include the link shown below; Snapshot does not inject it automatically.
<a href="/index/index.html" hidden aria-hidden="true" tabindex="-1">Snapshot root</a>
<script
id="snapshot-protocol"
data-site-version="1"
data-force-origin="false"
src="https://cdn.jsdelivr.net/npm/@magiccodingman/snapshot-protocol@latest/dist/snapshot-protocol.min.js">
</script>
When a route has reached its final state, render:
<snapshot-ready>
<title>Product One</title>
<meta name="description" content="Product One description">
<link rel="canonical" href="https://example.com/products/one">
<script type="application/ld+json">{ "@context": "https://schema.org" }</script>
</snapshot-ready>
Install the CLI:
dotnet tool install --global Snapshot.Cli
snapshot browser install
snapshot build ./publish/wwwroot --output ./site.snapshot.zip
The CLI package intentionally does not embed enormous platform-specific Playwright drivers or Chromium builds. Before the first browser operation it locates the exact Microsoft.Playwright driver in the NuGet cache, or downloads that matching package from NuGet's stable package endpoint into Snapshot's per-user cache. Chromium is provisioned separately, then a real browser launch is validated before rendering. Interrupted or incomplete driver and browser installations are detected and repaired.
Safe processing
The CLI validates and safely compacts generated snapshot pages by default. Processing applies only to manifest entries marked as real snapshots. It never modifies the developer's source /index.html, case aliases, prefix gateways, assets, or hosting artifacts.
Default processing:
- Requires exactly one absolute HTTP or HTTPS canonical URL.
- Requires the canonical path to match the captured route. The supplied domain is trusted.
- Validates and compacts inline JSON and JSON-LD.
- Conservatively compacts inline
<style>blocks and validates the output again. - Conservatively collapses HTML whitespace and removes ordinary HTML comments.
- Preserves conditional, crawler-control, and Snapshot Protocol comments.
- Re-parses transformed HTML and compares structural, executable-content, sensitive-text, and visible-text invariants.
- Falls back to the unminified snapshot whenever the transformed result cannot be proven equivalent.
Snapshot processing does not minify JavaScript, rename identifiers or selectors, rewrite filenames, combine files, tree-shake code, optimize SVGs, or compress images. Those application-build responsibilities remain outside Snapshot.
Disable only minification while retaining validation:
snapshot build ./wwwroot --no-minify
Relax canonical enforcement deliberately:
snapshot build ./wwwroot --canonical-policy warning
Existing Snapshot archives can use the same processor:
snapshot process ./site.snapshot.zip ./site.processed.snapshot.zip
See Safe processing for the complete policy and configuration.
.NET API
Install the executor and standard processing packages. Snapshot.Protocol is included transitively:
dotnet add package Snapshot.Playwright
dotnet add package Snapshot.Processing
using Snapshot.Playwright;
using Snapshot.Processing;
using Snapshot.Protocol.Build;
var engine = SnapshotEngine.CreateBuilder()
.UsePlaywright(options =>
{
options.Headless = true;
})
.UseStandardProcessing()
.Build();
var result = await engine.BuildAsync(new SnapshotBuildRequest
{
SourceDirectory = "./publish/wwwroot",
OutputPath = "./site.snapshot.zip",
Concurrency = 4
});
Snapshot.Protocol owns route discovery, casing rules, output planning, streaming ZIP creation, manifests, archive helpers, diagnostics, validation, and the processor contract. Snapshot.Playwright supplies the real browser executor. Snapshot.Processing supplies reusable validation and conservative transformation implementations.
Archive helpers
await using var archive = await SnapshotArchive.OpenAsync("site.snapshot.zip");
await foreach (var file in archive.EnumerateFilesAsync())
{
Console.WriteLine(file.Path);
}
await archive.StreamFilesAsync(async file =>
{
await destination.AddFileAsync(file.Path, file.Content, file.CancellationToken);
});
await using var completeZip = await archive.OpenArtifactReadStreamAsync();
You can also read one file, infer directories, inspect the manifest, safely extract the archive, find case-insensitive matches, or copy every entry through ISnapshotFileSink.
Route discovery
By default Snapshot Protocol recursively scans *.xml and *.xml.gz files in the source directory:
<urlset>files contribute page routes.<sitemapindex>files are recognized but are not mistaken for page lists.- Unrelated XML is ignored.
- Explicit routes can be added through the API or repeated
--routeoptions. robots.txtis not used as implicit discovery logic.
Query-string routes are rejected because distinct query URLs cannot safely map to one folder-style index.html without an explicit future strategy.
Casing and Windows targets
The default artifact uses case-sensitive route semantics even when built on Windows. ZIP entries can therefore preserve both canonical and compatibility paths.
For /MyPath, meaningful variants include:
/MyPath
/myPath
/mypath
/Mypath
/MYPATH
For extraction or hosting on an ordinary Windows filesystem, use:
snapshot build ./wwwroot --target-filesystem windows
Windows mode disables physical case aliases and validates that canonical paths can coexist safely. The default system does not allow Windows filesystem limitations to reduce Linux, Netlify, IPFS, or object-storage output.
Existing developer files always win. If the source already contains an index.html at a generated target, Snapshot Protocol preserves it unchanged and records the decision.
Netlify and ETags
Snapshot Protocol can generate and merge Netlify _headers and _redirects blocks:
snapshot build ./wwwroot --netlify
This support is explicitly Netlify-specific. Other hosts require their own provider implementations.
The hosted-site validator detects a dangerous deployment condition observed during real crawler testing: different HTML bodies being served with the same ETag.
snapshot validate-host ./site.snapshot.zip https://example.com
A correct ZIP does not guarantee that a host serves it correctly. Validate the deployed response behavior.
URL paths and IPFS
For a root-hosted SPA, prefer <base href="/"> and root-relative application paths such as /css/site.css. Document-relative paths such as css/site.css resolve beneath the current snapshot route.
IPFS subdomain gateways and DNSLink provide a proper application origin. Legacy path gateways such as /ipfs/CID/... do not preserve root-relative paths correctly and are not recommended for Snapshot Protocol applications.
Branch and release model
mainis active upstream development.releaseis the protected stable source.- NuGet and npm publishing workflows run only after promotion to
release. - GitHub releases and immutable tags remain manually authored archival milestones.
Documentation
- Protocol and message contract
- Root gateway
- Browser client
- Safe processing
- .NET API
- Playwright executor
- CLI reference
- Route discovery
- Casing and filesystems
- ZIP archive format
- Metadata and hydration
- Hosting and ETags
- Netlify
- IPFS and URL paths
- Testing
- Release process
License
Snapshot Protocol is licensed under the GNU Affero General Public License v3.0 only. See LICENSE.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Playwright (>= 1.61.0)
- Snapshot.Protocol (>= 0.1.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.1.0 | 79 | 7/27/2026 |