ForgeTrust.AppSurface.Web
0.2.0-preview.2
dotnet add package ForgeTrust.AppSurface.Web --version 0.2.0-preview.2
NuGet\Install-Package ForgeTrust.AppSurface.Web -Version 0.2.0-preview.2
<PackageReference Include="ForgeTrust.AppSurface.Web" Version="0.2.0-preview.2" />
<PackageVersion Include="ForgeTrust.AppSurface.Web" Version="0.2.0-preview.2" />
<PackageReference Include="ForgeTrust.AppSurface.Web" />
paket add ForgeTrust.AppSurface.Web --version 0.2.0-preview.2
#r "nuget: ForgeTrust.AppSurface.Web, 0.2.0-preview.2"
#:package ForgeTrust.AppSurface.Web@0.2.0-preview.2
#addin nuget:?package=ForgeTrust.AppSurface.Web&version=0.2.0-preview.2&prerelease
#tool nuget:?package=ForgeTrust.AppSurface.Web&version=0.2.0-preview.2&prerelease
AppSurface Web
The ForgeTrust.AppSurface.Web package provides the bootstrapping logic for building ASP.NET Core applications using the AppSurface module system. It sits on top of the compilation concepts defined in ForgeTrust.AppSurface.Core.
Overview
The easiest way to get started is by using the WebApp static entry point. This provides a default setup that works for most applications.
await WebApp<MyRootModule>.RunAsync(args);
For more advanced use cases where you need to customize the startup lifecycle beyond what the options provide, you can extend WebStartup<TModule>.
Release Guidance
AppSurface ships as a coordinated package family. Before installing this package from a prerelease feed, check the package chooser and release hub for current release risk, migration guidance, and readiness.
Key Abstractions
WebApp
The primary entry point for web applications. It handles creating the internal startup class and running the application. It provides a generic overload WebApp<TModule> for standard usage and WebApp<TStartup, TModule> if you have a custom startup class.
IAppSurfaceWebModule
Modules that want to participate in the web startup lifecycle should implement this interface. It extends IAppSurfaceHostModule and adds web-specific hooks:
ConfigureWebOptions: Modify the globalWebOptions(e.g., enable MVC, configure CORS).ConfigureWebApplication: Register pre-routing middleware usingIApplicationBuilder(e.g., exception handling or package-owned static-file middleware that must run before routing).ConfigureEndpointAwareMiddleware: Register middleware that must run after routing has selected an endpoint and before endpoints execute (e.g., root/host-ownedapp.UseAuthentication()andapp.UseAuthorization()).ConfigureEndpoints: Map endpoints usingIEndpointRouteBuilder(e.g.,endpoints.MapGet("/", ...)).
WebStartup
The base class for the application bootstrapping logic. While WebApp uses a generic version of this internally, you can extend it if you need deep customization of the host builder or service configuration logic.
Features
Health and Readiness Probes
AppSurface Web maps public platform probe endpoints by default:
/healthruns every registered ASP.NET Core health check./readyruns only checks tagged withAppSurfaceHealthCheckTags.Ready.
Both endpoints return minimal plain-text aggregate status (Healthy, Degraded, or Unhealthy), set no-store headers,
and return 200 only for Healthy. Degraded and Unhealthy return 503 so Kubernetes, Aspire, Docker, and similar
platforms can remove an instance from traffic. The endpoints are excluded from API Explorer/OpenAPI by default because
they are platform probes, not application API operations.
If no checks are tagged with AppSurfaceHealthCheckTags.Ready, /ready reports Healthy once the web app has started.
Add ready-tagged checks for dependencies that must be available before the app receives traffic.
3-minute health and readiness path
Register normal ASP.NET Core health checks from your module or host. Tag startup-critical checks with
AppSurfaceHealthCheckTags.Ready:
using ForgeTrust.AppSurface.Web;
using Microsoft.Extensions.Diagnostics.HealthChecks;
public sealed class MyRootModule : IAppSurfaceWebModule
{
public void ConfigureServices(StartupContext context, IServiceCollection services)
{
services.AddHealthChecks()
.AddCheck(
"database",
() => HealthCheckResult.Healthy("Database connection accepted."),
tags: [AppSurfaceHealthCheckTags.Ready]);
}
}
Run the app and verify the default routes:
curl -i http://127.0.0.1:5000/health
curl -i http://127.0.0.1:5000/ready
curl -I http://127.0.0.1:5000/ready
Use the endpoints as platform probes:
livenessProbe:
httpGet:
path: /health
port: http
periodSeconds: 10
timeoutSeconds: 2
readinessProbe:
httpGet:
path: /ready
port: http
periodSeconds: 5
timeoutSeconds: 2
Customize or disable the routes through WebOptions.Health:
await WebApp<MyRootModule>.RunAsync(
args,
options =>
{
options.Health.HealthPath = "/internal/live";
options.Health.ReadyPath = "/internal/ready";
// options.Health.Enabled = false; // Use this when the host owns probe endpoints directly.
});
Important behavior:
- Probe paths must be app-root-relative paths without route parameters, query strings, fragments, whitespace, or traversal segments.
HealthPathandReadyPathmust be distinct after normalization.- AppSurface fails startup if a module or
WebOptions.MapEndpointsalready mapped the configured probe path. Move the existing endpoint, changeWebOptions.Healthpaths, or disable AppSurface health endpoints. - The endpoints are intentionally public and unauthenticated so infrastructure probes can reach them. Keep them behind normal platform controls such as cluster-local networking, ingress allowlists, or rate limiting when they are exposed beyond private service traffic.
- Public probes expose only aggregate status. Use authenticated support surfaces, such as Config audit diagnostics, for detailed operator evidence.
- The product-readiness lab's
/readinessendpoint is an example-local report endpoint. It is separate from the reusable AppSurface Web/readyplatform probe contract.
PWA Install Metadata
AppSurface Web can serve the baseline Progressive Web App install contract from WebOptions.Pwa: a manifest endpoint, MVC/Razor head tags, development diagnostics, and an explicit starter offline service worker. PWA support is disabled by default, and offline caching stays off until the app configures an offline strategy.
3-minute PWA install path
Configure install metadata in the existing Web package:
await WebApp<MyRootModule>.RunAsync(
args,
options =>
{
options.Mvc = options.Mvc with { MvcSupportLevel = MvcSupport.ControllersWithViews };
options.Pwa.Enabled = true;
options.Pwa.Name = "Contoso Field Notes";
options.Pwa.ShortName = "Field Notes";
options.Pwa.ThemeColor = "#2563eb";
options.Pwa.BackgroundColor = "#ffffff";
options.Pwa.Icons.Add(new PwaIcon { Source = "/icons/app-192.png", Sizes = "192x192", Type = "image/png" });
options.Pwa.Icons.Add(new PwaIcon { Source = "/icons/app-512.png", Sizes = "512x512", Type = "image/png" });
});
Add the head helper to your MVC layout:
<appsurface:pwa-head />
Run the app on HTTPS or localhost, then verify the install contract:
appsurface pwa verify --url https://app.example.com
AppSurface maps /manifest.webmanifest with application/manifest+json, exposes development-only diagnostics at /_appsurface/pwa, and emits no service worker unless options.Pwa.Offline.Enabled is true. Minimal API and custom-layout apps can copy the exact generated tags from /_appsurface/pwa instead of using the TagHelper.
For the full API shape, browser caveats, offline pitfalls, and CLI proof flow, see PWA install support. For executable proof, run examples/web-pwa-install.
Middleware Lifecycle
AppSurface Web keeps middleware and endpoint mapping in three explicit phases:
ConfigureWebApplicationruns before static files, routing, AppSurface CORS, and endpoint execution. Use it for middleware that does not need endpoint metadata.ConfigureEndpointAwareMiddlewareruns afterUseRouting()and AppSurface-managed CORS, before endpoint execution. At request time, middleware here can inspectHttpContext.GetEndpoint(), route values, and endpoint metadata for matched endpoints.ConfigureEndpointsmaps module endpoints.WebOptions.MapEndpointsmaps direct host endpoints in the same endpoint-routing phase.
Global ASP.NET Core authentication and authorization middleware should be registered once from the root or host integration module:
public sealed class MyRootModule : IAppSurfaceWebModule
{
public void ConfigureServices(StartupContext context, IServiceCollection services)
{
services.AddAuthentication(/* scheme configuration */);
services.AddAuthorization();
}
public void ConfigureEndpointAwareMiddleware(StartupContext context, IApplicationBuilder app)
{
app.UseAuthentication();
app.UseAuthorization();
}
}
The root/host module's endpoint-aware middleware runs before dependency feature modules, so feature middleware can rely on root-owned authentication having already run. Feature modules should only register endpoint-aware middleware they own; they should not each install global authentication or authorization middleware. Apps that need bespoke pipeline policy beyond AppSurface's module hooks can use a custom WebStartup<TModule>.
Endpoint-aware middleware must tolerate HttpContext.GetEndpoint() being null for unmatched requests. Do not call UseRouting, UseCors, UseEndpoints, or map endpoints from this hook.
MVC and Controllers
Support for MVC approaches can be configured via WebOptions:
- None: For pure Minimal APIs (default).
- Controllers: For Web APIs using controllers (
AddControllers). - ControllersWithViews: For traditional MVC apps with views.
- Full: Full MVC support.
CORS
Built-in support for CORS configuration:
- Enforced Origin Safety: When
EnableCorsis true, you MUST specify at least one origin inAllowedOrigins, unless running in Development withEnableAllOriginsInDevelopmentenabled (the default). IfAllowedOriginsis empty in production or whenEnableAllOriginsInDevelopmentis disabled, the application will throw a startup exception to prevent unintended security openness (verified by testsEmptyOrigins_WithEnableCors_ThrowsExceptionandEnableAllOriginsInDevelopment_AllowsAnyOrigin). A literal origin wildcard (["*"]) is also rejected outside Development for AppSurface-managed CORS; use explicit browser origins such as["https://app.example.com"]. - Development Convenience:
EnableAllOriginsInDevelopment(enabled by default) automatically allows any origin when the environment isDevelopment. WhenAllowedHeadersandAllowedMethodsare empty or omitted, theDefaultCorsPolicyalso allows any header and method for local convenience; configured values keep those header and method restrictions enforced even in Development. - Origin Wildcards Are Not All The Same:
AllowedOrigins = ["*"]means every origin and is only available through the development compatibility path.AllowedOrigins = ["https://*.example.com"]is ASP.NET Core wildcard-subdomain matching and remains supported. Host binding wildcards such ashttp://*:5000decide which network interfaces Kestrel listens on; they do not make browser CORS responses public. - Header and Method Control:
AllowedHeadersandAllowedMethodsdefault to empty arrays in production, so AppSurface does not silently allow every preflight header and method once CORS is enabled. Set each collection to the browser contract the app actually supports, for example["Content-Type", "X-Request-Id"]and[HttpMethods.Get, HttpMethods.Post]. Use["*"]only when a production app intentionally wantsAllowAnyHeader()orAllowAnyMethod(). - Default Policy: Configures a policy named "DefaultCorsPolicy" (configurable) and automatically registers the CORS middleware.
await WebApp<MyRootModule>.RunAsync(
args,
options =>
{
options.Cors.EnableCors = true;
options.Cors.AllowedOrigins = ["https://app.example.com"];
options.Cors.AllowedHeaders = ["Content-Type", "X-Request-Id"];
options.Cors.AllowedMethods = [HttpMethods.Get, HttpMethods.Post];
});
The production default is deliberately stricter than older AppSurface previews: AllowedOrigins decides which browser
frontends may read cross-origin responses, while AllowedHeaders and AllowedMethods decide which preflighted browser
requests are accepted from those origins. Keep them explicit for production APIs, especially when credentials are
allowed. Migrate older permissive production configuration from:
options.Cors.EnableCors = true;
options.Cors.AllowedOrigins = ["*"];
to:
options.Cors.EnableCors = true;
options.Cors.AllowedOrigins = ["https://app.example.com"];
For an intentionally public API that must own wildcard CORS in production, do not use the AppSurface-managed policy for that surface. Register and apply the host's ASP.NET Core CORS policy directly so the public contract is visible in the application's own startup code.
Endpoint Routing
Modules can define their own endpoints, making it easy to slice features vertically ("Vertical Slice Architecture").
Config Audit HTTP Diagnostics
MapAppSurfaceConfigAuditDiagnostics maps an authenticated GET endpoint that returns the active host's sanitized
ConfigAuditReport JSON. The endpoint is support-sensitive operator evidence capture: it can expose provider names,
configuration keys, source paths, diagnostics, and redaction metadata even though values are already sanitized by
ForgeTrust.AppSurface.Config.
The endpoint is never mapped automatically. The default route is
/_appsurface/config/audit (AppSurfaceConfigAuditDiagnosticsDefaults.DefaultRoute), and hosts can pass a custom route
when their operations model keeps diagnostics under a different path.
Config Audit HTTP in 5 minutes
Add the Config module, configure normal ASP.NET Core authentication and authorization, install the authorization middleware, then opt in to the endpoint:
public sealed class MyRootModule : IAppSurfaceWebModule
{
public void RegisterDependentModules(ModuleDependencyBuilder builder)
{
builder.AddModule<AppSurfaceConfigModule>();
}
public void ConfigureServices(StartupContext context, IServiceCollection services)
{
services.AddAuthentication(/* host scheme */);
services.AddAuthorization(options =>
{
options.AddPolicy("ConfigAuditRead", policy => policy.RequireAuthenticatedUser());
});
}
public void ConfigureHostBeforeServices(StartupContext context, IHostBuilder builder)
{
}
public void ConfigureHostAfterServices(StartupContext context, IHostBuilder builder)
{
}
public void ConfigureEndpointAwareMiddleware(StartupContext context, IApplicationBuilder app)
{
app.UseAuthentication();
app.UseAuthorization();
}
public void ConfigureEndpoints(StartupContext context, IEndpointRouteBuilder endpoints)
{
endpoints.MapAppSurfaceConfigAuditDiagnostics("ConfigAuditRead");
}
}
Capture the report only under your support policy:
curl -H "Authorization: Bearer $SUPPORT_TOKEN" \
https://app.example.com/_appsurface/config/audit \
> production.config-audit.json
Compare captured host snapshots later with the Config package's captured-snapshot diff workflow: Config diff in 10 minutes.
Important behavior:
- Mapping validates a non-null endpoint builder plus non-blank route and policy names at startup.
- The endpoint maps
GETonly, appliesRequireAuthorization(policyName), and is hidden from API Explorer/OpenAPI by default withExcludeFromDescription(). - Successful responses and AppSurface-owned setup/runtime failures set
Cache-Control: no-storeandPragma: no-cache. - Missing Config services, missing environment services, blank environment names, reporter failures, and report JSON
failures return safe
500ProblemDetails withproblem,cause,fix, anddocsLinkextensions. - Native ASP.NET Core authorization still owns challenge and forbid behavior. Cookie hosts may redirect on unauthorized
requests unless the host's auth scheme is configured for API-style
401/403responses. - Missing authorization policies or missing authorization middleware fail closed through ASP.NET Core's own runtime behavior before the handler runs.
- AppSurface Web does not add rate limiting. Put host-owned rate limiting, network controls, audit logging, or break-glass workflow around this route when support access requires it.
For custom JSON options, ProblemDetails shape, discovery behavior, rate limiting, or auth response formatting, write a
host-owned MapGet endpoint over IConfigAuditReporter instead of using this package mapper.
Browser Status Pages
AppSurface Web includes conventional browser-facing pages for empty 401, 403, and 404 status responses. The feature is designed for human browser requests: it keeps the original HTTP status code, shows recovery-oriented HTML, and leaves JSON/API responses alone.
Browser status pages in 2 minutes
If your app already uses MVC views, keep the default Auto mode:
public void ConfigureWebOptions(StartupContext context, WebOptions options)
{
options.Mvc = options.Mvc with { MvcSupportLevel = MvcSupport.ControllersWithViews };
}
If your app starts with controllers only but still wants the browser pages, opt in explicitly:
public void ConfigureWebOptions(StartupContext context, WebOptions options)
{
options.Mvc = options.Mvc with { MvcSupportLevel = MvcSupport.Controllers };
options.Errors.UseConventionalBrowserStatusPages();
}
Preview the built-in pages while the app is running:
| Status | Preview URL |
|---|---|
401 |
/_appsurface/errors/401 |
403 |
/_appsurface/errors/403 |
404 |
/_appsurface/errors/404 |
Override any page with the conventional app or shared Razor Class Library path:
| Status | Override path |
|---|---|
401 |
~/Views/Shared/401.cshtml |
403 |
~/Views/Shared/403.cshtml |
404 |
~/Views/Shared/404.cshtml |
Use BrowserStatusPageModel in overrides:
@model ForgeTrust.AppSurface.Web.BrowserStatusPageModel
<h1>HTTP @Model.StatusCode</h1>
<p>@Model.OriginalPath</p>
Mode behavior:
| Mode | Behavior |
|---|---|
BrowserStatusPageMode.Auto |
Enables browser status pages only when MVC support already includes views. |
BrowserStatusPageMode.Enabled |
Always enables browser status pages and upgrades MVC support to controllers with views when needed. |
BrowserStatusPageMode.Disabled |
Leaves status-code handling fully to the app or other middleware. |
Important behavior:
- Only empty
401,403, and404responses fromGETorHEADrequests that accepttext/htmlorapplication/xhtml+xmlare re-executed. - JSON, non-HTML, non-empty, and non-GET/HEAD responses keep their original API-friendly behavior.
- The framework fallback is app-agnostic. It includes a generic home recovery link and does not inspect product-specific route families such as
/docs. - Apps that need domain-specific recovery, such as documentation search for stale docs links, should add
~/Views/Shared/404.cshtmland render links from their own route contracts. - Static export remains conservative: RazorWire CLI probes
/_appsurface/errors/404and writes only404.html; it does not emit401.htmlor403.html. In CDN mode, that404.htmlpage is validated and rewritten with the rest of the static output. The fallbackReturn homelink is markeddata-rw-export-ignoreso apps that do not export/can still publish a valid conventional404.html. - Production
500exception pages are intentionally separate from browser status pages and must be enabled withUseConventionalExceptionPage().
Conventional Production 500 Pages
AppSurface Web can also own a safe browser-facing production 500 page for unhandled exceptions. This is off by default because exception handling is usually application policy. Opt in through WebOptions.Errors.UseConventionalExceptionPage() when a normal MVC-style app wants a generic HTML failure page without writing exception middleware by hand.
await WebApp<MyRootModule>.RunAsync(
args,
options => options.Errors.UseConventionalExceptionPage());
The conventional exception page uses ASP.NET Core exception handling, not status-code pages. That distinction matters: status-code pages can render empty 404 or 500 responses, but they do not catch thrown exceptions. AppSurface registers the exception handler early enough to catch module middleware and endpoint failures, then renders a Razor view only for requests that accept text/html or application/xhtml+xml.
- The default view returns HTTP
500, generic recovery copy, a home link, and a request id that operators can correlate with logs. - The default
ExceptionPageModelcontains onlyStatusCodeandRequestId; it does not expose exception messages, stack traces, headers, cookies, route values, or form fields. - Applications can override the page with
~/Views/Shared/500.cshtml. Keep the page generic and support-oriented. Do not readIExceptionHandlerFeature, request headers, cookies, route values, or form values unless the app has a deliberate reviewed disclosure policy. - Development keeps its existing developer exception behavior. AppSurface does not install the conventional production handler when
StartupContext.IsDevelopmentis true. - API-only apps, JSON problem-details APIs, tenant-specific error pages, or apps with telemetry-first exception middleware should leave this disabled and register their own exception handling.
- Once ASP.NET Core has started a response, exception handling cannot replace it with the conventional page. Design streaming endpoints so failures are reported through the stream protocol rather than relying on a late 500 page.
Executable error-page proof
Use the focused web error-page proof when you want executable evidence rather than API reference prose:
bash examples/web-error-pages/verify.sh
The proof starts a local production-mode app, verifies browser HTML for empty 401, 403, 404, and thrown 500 paths, verifies API requests do not receive browser HTML, and checks that synthetic request sentinels are absent from the production 500 response body.
Configuration and Port Overrides
The web application supports standard ASP.NET Core configuration sources (command-line arguments, environment variables, and appsettings.json).
Deterministic Development Port Default
When an AppSurface web application starts in Development without explicit endpoint configuration, AppSurface Web chooses a deterministic localhost-only fallback URL based on the current workspace path. That gives each local worktree a stable URL instead of every development environment fighting for the same hard-coded dev port.
- Use this default for local
dotnet runconvenience when you do not care about a specific port ahead of time. - AppSurface treats
--environment Development,ASPNETCORE_ENVIRONMENT=Development, andDOTNET_ENVIRONMENT=Developmentas development for both the deterministic port resolver and module-levelStartupContext.IsDevelopmentdecisions. Command-line environment parsing is shared withDefaultEnvironmentProvider, so duplicate--environmentkeys use the last valid value. - Override it any time with
--port,--urls,ASPNETCORE_URLS/URLS,ASPNETCORE_HTTP_PORTS/DOTNET_HTTP_PORTS/HTTP_PORTS,ASPNETCORE_HTTPS_PORTS/DOTNET_HTTPS_PORTS/HTTPS_PORTS,urls/http_ports/https_portsin appsettings, orKestrel:Endpointsin appsettings/environment variables. - Treat the startup log as the source of truth for the selected local URL.
- The automatic fallback and
--portshortcut bind onlyhttp://localhost:{port}. Add--all-hoststo the--portshortcut, or pass an explicit wildcard URL, only when you intentionally need LAN/container access.
Port Overrides
You can override the application's listening port using several methods:
Command-Line: Use
--port(localhost-only shortcut),--portwith--all-hosts, or--urls.dotnet run -- --port 5001 # OR dotnet run -- --port 5001 --all-hosts # OR dotnet run -- --urls "http://localhost:5001"Environment Variables: Set
ASPNETCORE_URLS.export ASPNETCORE_URLS="http://localhost:5001" dotnet runApp Settings: Configure
urlsinappsettings.json.{ "urls": "http://localhost:5001" }Kestrel Endpoints: Configure named endpoints when you need protocol, certificate, or endpoint-specific settings.
{ "Kestrel": { "Endpoints": { "Http": { "Url": "http://localhost:5001" } } } }
The --port flag is a convenience shortcut that maps to http://localhost:{port}. Add --all-hosts to map the same port to http://localhost:{port};http://*:{port} when all-interface access is intentional. The * host is a wildcard binding and can expose the preview host beyond the local machine. If both --port and --urls are provided, --port takes precedence.
[!TIP]
If you rely on the deterministic development-port fallback, different worktrees on the same machine will get different stable ports. If you need a predictable shared URL for docs, QA, or CI instructions, pass --port or --urls explicitly instead of depending on the fallback.
Startup Watchdog
AppSurface Web fails fast when a host does not complete startup within WebOptions.StartupTimeout. The default is 10 seconds. This catches pre-bind stalls where the process is alive but Kestrel has not started listening, including sandbox restrictions, package layout issues, static web asset discovery hangs, and hosted services that block startup.
When the watchdog fires, AppSurface logs the observed startup phase, current directory, application base directory, static web asset mode, endpoint-related startup arguments, and any known Codex sandbox markers such as CODEX_SANDBOX. If a Codex sandbox is detected, try the same command outside the sandbox or with the runner's approved unsandboxed/escalated permission before debugging package layout or hosted-service startup.
Configure or disable the watchdog through WebOptions:
await WebApp<MyRootModule>.RunAsync(
args,
options => options.StartupTimeout = TimeSpan.FromSeconds(60));
Set StartupTimeout to null only when the host intentionally performs long-running pre-bind work. Values at or below zero are invalid; use null instead of TimeSpan.Zero when disabling the guard. The watchdog stops checking once startup completes, so it does not limit normal request processing or long-running background work after the host is listening.
| 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
- CliWrap (>= 3.10.1)
- ForgeTrust.AppSurface.Config (>= 0.2.0-preview.2)
- ForgeTrust.AppSurface.Core (>= 0.2.0-preview.2)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on ForgeTrust.AppSurface.Web:
| Package | Downloads |
|---|---|
|
ForgeTrust.RazorWire
ForgeTrust.RazorWire package for AppSurface application composition. |
|
|
ForgeTrust.AppSurface.Web.OpenApi
ForgeTrust.AppSurface.Web.OpenApi package for AppSurface application composition. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.2.0-preview.2 | 350 | 7/3/2026 |
| 0.2.0-preview.1 | 324 | 6/28/2026 |
| 0.1.0 | 141 | 7/2/2026 |
| 0.1.0-rc.4 | 287 | 6/16/2026 |
| 0.1.0-rc.3 | 193 | 6/8/2026 |
| 0.1.0-rc.2 | 90 | 6/3/2026 |
| 0.1.0-rc.1 | 78 | 5/31/2026 |
| 0.1.0-preview.4 | 77 | 5/25/2026 |
| 0.1.0-preview.3 | 76 | 5/20/2026 |
| 0.1.0-preview.2 | 82 | 5/14/2026 |