string Display { get; }
Source
Gets a compact diagnostic description of the classification evidence.
A static generation engine that crawls a RazorWire application and exports its routes to CDN or hybrid static files.
Task FinalizeAppSurfaceDocsRouteManifestAsync(ExportContext context, CancellationToken cancellationToken)
Replaces private route-manifest staging URLs with guarded paths to the ordinary HTML files that were actually materialized, before the release archive writer binds the final manifest bytes.
Task RunAsync(ExportContext context, CancellationToken cancellationToken = default)
Crawls the site starting from configured seed routes (or the root), stages the conventional reserved 404 page when available, validates CDN output when requested, and exports discovered pages, frame sources, and assets to the output path.
context
cancellationToken
A task that completes when the crawl and export operations have finished.
FileNotFoundException
ExportContext.SeedRoutesPath
is specified but the file does not exist.ExportContext.SeedRoutesPath
is provided, the file is read and each line is validated and normalized to a root-relative route; invalid seeds are logged. If the seed file exists but yields no valid routes, the root path ("/") is enqueued. If no seed file is provided, ExportContext.InitialSeedRoutes
is used when present; invalid in-memory seeds are logged and an all-invalid set also falls back to the root path. If neither source is provided, the root path is enqueued. Before crawl processing begins, the engine probes AppSurface's reserved conventional 404 route and stages 404.html
when the route returns a successful HTML response. That reserved-route probe is best-effort only: failures are logged, do not abort the crawl, and do not prevent queued seed routes from being processed. Once staged, the 404.html
body participates in the same CDN validation and reference rewriting as other HTML artifacts. Export then runs as a staged pipeline: seed queue
-> crawl/fetch/discover
-> validate canonical artifacts and redirect aliases
-> materialize/rewrite text routes
-> redirect strategy
|-- html: write alias HTML fallback artifacts
`-- netlify: write root _redirects rules
The crawl stage records route outcomes, artifact URLs, and reference provenance. In CDN mode, HTML and CSS bodies are kept once until materialization so managed URLs can be rewritten after the artifact map is complete. In hybrid mode, text artifacts and binary assets are written directly to their final files and only their outcomes are retained in memory. Redirect alias registrations are validated before text materialization, then written according to ExportContext.RedirectStrategy
after canonical artifacts exist. Netlify redirect output is an exact publish-root rule file and does not use ExportContext.BaseUrl
or emitted artifact URLs. Task MaterializeAppSurfaceDocsArchiveRootAssetsAsync(ExportContext context, CancellationToken cancellationToken)
Promotes assets from the one managed docs index's source folder. With no managed index, preserves the legacy prepopulated default docs handoff, including its already-authored index; all copies retain physical guards and distinct root names. Managed indexes use independent generic and frozen projections instead of mirroring.
Task CrawlRouteAsync(HttpClient client, string route, ExportContext context, CancellationToken cancellationToken)
Fetches the HTML or asset for the specified route, records export graph metadata, and enqueues discovered managed references.
void QueueSeedRoutes(ExportContext context, IEnumerable<string> seeds, CancellationToken cancellationToken)
Validates, normalizes, and enqueues seed routes from a file or in-memory route collection.
context
seeds
cancellationToken
string StripAppSurfaceDocsDiagnosticsChrome(string html)
Removes AppSurface Docs maintainer diagnostics chrome from export HTML before link discovery or artifact writes.
html
The HTML with marked AppSurface Docs diagnostics chrome removed.
StripAppSurfaceDocsDiagnosticsChrome
returns null or empty input unchanged, then performs an ordinal case-insensitive marker precheck against AppSurfaceDocsDiagnosticsChromeAttributeName
before applying a global Regex.Replace(string, string)
with AppSurfaceDocsDiagnosticsChromeRegex
. All matching diagnostics disclosures are removed.
The method operates on raw HTML rather than a parsed DOM, so marker-like text in unexpected locations can be removed. The static compiled regex is thread-safe for concurrent exports, but it intentionally trades parser-level correctness for lightweight export-time cleanup and inherits the regex constraints documented on AppSurfaceDocsDiagnosticsChromeRegex
.
bool IsDocsExportPage(string route, string html, string? docContentFrame = null)
Determines whether an exported HTML page should receive AppSurfaceDocs static partial support.
route
html
docContentFrame
doc-content
frame, when the caller has already parsed it.
true
for the legacy /docs
route family or HTML that carries AppSurfaceDocs runtime markers; otherwise false
.
Custom AppSurfaceDocs hosts can mount under route families such as /foo/bar
, so export detection cannot rely only on path prefixes. The client config marker covers search and shell pages, while the content frame covers document detail pages.
string MapRouteToFilePath(string route, string outputPath, bool isHtml)
Maps a root-relative route to an absolute file path inside the configured output directory.
void ExtractLinks(string html, ExportContext context, string currentRoute = "/")
Extracts root-relative internal link targets from the provided HTML and enqueues any unvisited routes for crawling.
html
context
currentRoute
void ExtractFrames(string html, ExportContext context, string currentRoute = "/")
Extracts root-relative `src` values from <turbo-frame> elements in the provided HTML and enqueues each unvisited path for export.
html
context
currentRoute
void ExtractAssets(string html, string currentRoute, ExportContext context)
Extracts root-relative asset references (scripts, styles, images) from the provided HTML and enqueues each unvisited path for export.
html
currentRoute
context
IReadOnlyList<ExportReference> ExtractReferences(string content, string currentRoute, bool htmlScope)
Extracts exporter-managed internal references from HTML or CSS content.
content
currentRoute
content
, used to resolve relative URLs and record provenance.htmlScope
true
scans HTML surfaces including anchors, Turbo Frames, scripts, supported link tags, image sources, srcset
candidates, style blocks, and style attributes. Anchors marked with data-rw-export-ignore
, and relative anchors pointing at common source or project file extensions, are skipped so authoring-only source-navigation links can remain clickable without becoming CDN dependencies. false
scans only CSS url(...)
references. References with managed root-relative paths only. External URLs, protocol-relative URLs, hash-only references, data URLs, JavaScript URLs, mailto links, and malformed values are filtered out before the caller enqueues or validates them.
string ResolveRelativeUrl(string baseRoute, string url)
Resolves a potentially relative URL against a base route.
IReadOnlyList<RecommendationReference> ReadRecommendationReferences(JsonObject root, string route, ExportContext context)
Parses complete next-read rows and schedules their targets, including pages absent from the search corpus. The original complete URL is checked before normalization; queries are discarded and approved fragments retained. The exporter owns this wire boundary and deliberately has no dependency on the Docs package.
bool TryValidateRecommendationUrl(string value, string root, out string path, out string fragment)
Validates the original JSON URL before URI normalization can erase dot segments. Retains the existing exporter origin/escaped-path guards and rejects controls, malformed escapes and single/double encoded path hazards.
string RewriteRecommendationIndex(string body, string route, ExportContext context, bool frozenProjection = false)
Rewrites each next-read only after successful same-tree HTML materialization is known. Frozen archives keep stable /docs-rooted artifact URLs, ordinary static output uses emitted URLs; identities and explanations stay intact. No partial JSON file is written if a target failed, points off-tree or was not materialized.
string ProjectArchiveSearchIndexArtifactUrl(ExportContext context, string artifactUrl)
Projects emitted artifact URLs into the stable archive root for the frozen search-index JSON contract. Ordinary HTML, frame, and alias artifacts keep their export-root URLs for generic static hosts.
Executes child processes for the export pipeline while preserving a structured ProcessResult
contract for callers.
This abstraction exists so resolver logic can verify command composition without launching real processes in tests. Callers should prefer it whenever they need stdout, stderr, exit code, and cancellation behavior surfaced in a consistent shape.
Task<ProcessResult> ExecuteCommandAsync(string fileName, IReadOnlyList<string> args, string workingDirectory, CancellationToken cancellationToken)
Executes a command and captures its exit code, standard output, and standard error.
fileName
args
fileName
.workingDirectory
cancellationToken
A ProcessResult
whose ExitCode
, Stdout
, and Stderr
describe the completed command or a start-up failure.
Implementations should avoid throwing for ordinary process start failures so higher-level callers can decide whether to surface an exception, retry, or fall back. Cancellation should still propagate via OperationCanceledException
.
Captures nearby link
attributes used to classify a supported href
reference.
Rel
rel
attribute value.As
as
attribute value, when present.string Display { get; }
Source
Gets a compact diagnostic description of the classification evidence.
Describes how to launch an external process.
string FileName { get; init; }
Source
Gets the executable file name.
IReadOnlyList<string> Arguments { get; init; }
Source
Gets the argument tokens passed to the process.
IReadOnlyDictionary<string, string> EnvironmentOverrides { get; init; }
Source
Gets environment variable overrides applied for process startup.
string WorkingDirectory { get; init; }
Source
Gets the working directory for the process.
Structured command execution result used by the export resolver pipeline.
ExitCode
Stdout
Stderr
The resolver prefers this explicit result shape over exceptions for ordinary command failures so it can preserve stdout/stderr in logs and decide whether to throw, fall back to XML parsing, or continue probing.
Resolves export sources and, when needed, orchestrates launching a target application for crawling.
Task<ResolvedExportSource> ResolveAsync(ExportSourceRequest request, CancellationToken cancellationToken = default)
Resolves a validated source request into a crawlable base URL, launching the target app when needed.
request
cancellationToken
A resolved source that owns any launched process for the lifetime of the export.
CommandException
TimeoutException
ListeningUrlTimeout
or does not become ready within AppReadyTimeout
. InvalidOperationException
OperationCanceledException
cancellationToken
is canceled while publishing, launching, or probing the source.
URL sources are probed for readiness and returned without starting a process. Project sources are published first unless ExportSourceRequest.NoBuild
is set, then launched with an ephemeral loopback binding when no explicit URL argument is present. DLL sources are launched directly. The returned ResolvedExportSource
must be disposed by the caller so any process launched for the export is stopped after crawling. If the resolver cannot observe a listening URL or the app does not become reachable before the configured timeouts, any launched process is disposed before the exception is rethrown.
Task<ProcessResult> ExecuteProcessAsync(string fileName, IReadOnlyList<string> args, string workingDirectory, CancellationToken cancellationToken)
Executes a command through the configured ICommandExecutor
.
fileName
args
workingDirectory
cancellationToken
A ProcessResult
containing the exit code and captured output streams from the delegated executor.
This seam exists so resolver tests can verify command composition without launching real processes. Callers should treat a non-zero exit as data and decide whether to raise an exception or fall back.
Task<string> TryResolveAssemblyNameAsync(string projectPath, string fallbackName, string? framework, CancellationToken cancellationToken)
Resolves the effective assembly name for a project, preferring MSBuild evaluation and falling back to raw project XML parsing when needed.
projectPath
fallbackName
framework
AssemblyName
values aligned with the publish target. cancellationToken
The assembly name reported by MSBuild when available; otherwise the value returned by TryResolveAssemblyNameFromXml
.
OperationCanceledException
MSBuild is preferred because it evaluates imports and conditional properties that raw XML parsing cannot see. If MSBuild exits non-zero, produces no usable stdout, or throws a non-cancellation exception, the resolver logs the failure details and falls back to XML parsing. Omitting framework
can mis-evaluate conditional AssemblyName
declarations for multi-target projects.
string TryResolveAssemblyNameFromXml(string projectPath, string fallbackName)
Reads a project file directly and returns the first explicit AssemblyName
value found in the XML.
projectPath
fallbackName
The explicit AssemblyName
from the project file, or fallbackName
when no usable value is available.
This method intentionally does not evaluate imports or conditional MSBuild properties. It is the low-cost fallback used when MSBuild evaluation is unavailable or fails to return a usable value.
TimeSpan ListeningUrlTimeout { get; set; }
Source
Gets or sets the maximum time to wait for the launched target app to emit a listening URL.
TimeSpan AppReadyTimeout { get; set; }
Source
Gets or sets the maximum time to wait for the launched target app to respond as ready.
TimeSpan AppReadyPollInterval { get; set; }
Source
Gets or sets the polling interval used while probing target app readiness.
Describes where an exporter-managed reference was found before URL normalization.
Provenance is internal CLI metadata used to make export diagnostics and maintainer logs actionable without exposing a new public API. Offsets and line numbers are best-effort source positions in the owning HTML or CSS body; they may be null
when the parser can identify a reference but the original source span cannot be located safely.
string DisplaySource { get; }
Source
Gets a compact developer-facing description such as <img src>
or style url() <style>
.
Provides a testable wrapper around the RazorWire CLI top-level entrypoint.
Task RunAsync(string[] args, Action<ConsoleOptions>? configureOptions = null)
Runs the RazorWire CLI using the same command-first startup path as the shipped program entrypoint.
args
configureOptions
A task that completes when CLI execution finishes.
IDisposable PushConfigureOptionsOverrideForTests(Action<ConsoleOptions> configureOptions)
Temporarily injects additional console startup configuration for the duration of a test that invokes the real assembly entrypoint.
configureOptions
A disposable scope that restores the previous override when disposed.
Records the fetch and materialization state for one normalized export route.
ExportRouteOutcome Success(string route, string? contentType, string artifactPath, string artifactUrl, string? textBody)
Creates a successful route outcome with emitted artifact details.
route
/
or /docs/start
.contentType
null
when the server omitted it.artifactPath
artifactUrl
textBody
null
for streamed assets.
An outcome with Succeeded
set to true
, artifact fields populated, and no status code or exception.
ExportRouteOutcome Success(string route, string? contentType, string artifactPath, string artifactUrl)
Creates a successful route outcome for an artifact whose body does not need deferred validation or rewriting.
route
/
or /docs/start
.contentType
null
when the server omitted it.artifactPath
artifactUrl
An outcome with Succeeded
set to true
, artifact fields populated, and no retained body.
ExportRouteOutcome RedirectAliasArtifact(string route, string artifactPath, string artifactUrl)
Creates a successful outcome for a generated redirect alias artifact.
route
artifactPath
artifactUrl
A successful HTML outcome marked as a redirect alias artifact.
ExportRouteOutcome NonSuccess(string route, HttpStatusCode statusCode)
Creates an outcome for a route that completed at the HTTP layer with a non-success status code.
route
statusCode
An outcome with Succeeded
set to false
, StatusCode
populated, and no artifact or exception fields.
ArgumentException
route
is null, empty, or whitespace. ArgumentOutOfRangeException
statusCode
is a successful 2xx HTTP status code. ExportRouteOutcome Failed(string route, Exception exception)
Creates an outcome for a route that failed because an exception interrupted fetch or write processing.
route
exception
An outcome with Succeeded
set to false
, Exception
populated, and no status or artifact fields.
string Route { get; }
Source
Gets the normalized root-relative route that was fetched.
bool Succeeded { get; }
Source
Gets a value indicating whether the route fetched successfully.
string? ContentType { get; }
Source
Gets the response media type when one was available, optionally including media-type parameters.
HttpStatusCode? StatusCode { get; }
Source
Gets the non-success response status code when the fetch failed at the HTTP layer.
string? ArtifactPath { get; }
Source
Gets the absolute output file path for a successful route.
string? ArtifactUrl { get; }
Source
Gets the static-host URL that should be used to reach the emitted artifact.
string? TextBody { get; }
Source
Gets the fetched HTML or CSS body retained until materialization.
Exception? Exception { get; }
Source
Gets the exception that prevented the route from being fetched or written.
bool IsRedirectAliasArtifact { get; }
Source
Gets a value indicating whether this outcome was written as a redirect alias artifact.
bool IsHtml { get; }
Source
Gets a value indicating whether the route was fetched as HTML.
bool IsCss { get; }
Source
Gets a value indicating whether the route was fetched as CSS.
Writes the AppSurface Docs release archive manifest for a completed static export.
The manifest is emitted after export materialization so it records final on-disk bytes, including rewritten HTML, redirect artifacts, binary assets, and hidden control files. The manifest itself is excluded from its file list so callers can pin the manifest digest in trusted host configuration without creating a self-referential payload.
Task<ReleaseArchiveManifestSummary> WriteAsync(string outputPath, CancellationToken cancellationToken)
Writes the release archive manifest beneath the export output directory.
outputPath
cancellationToken
A summary containing the manifest path and digest operators should pin in the version catalog.
Summarizes a release archive manifest emitted by static export.
ManifestPath
Schema
FileCount
Sha256
Creates validated export source requests from CLI options.
ExportSourceRequest Create(string? baseUrl, string? projectPath, string? dllPath, string? framework, IReadOnlyList<string> appArgs, bool noBuild)
Creates a validated export source request from mutually exclusive CLI source options.
baseUrl
projectPath
dllPath
framework
projectPath
and is ignored for URL and DLL sources. appArgs
noBuild
projectPath
sources. A validated source request.
CommandException
Exactly one of baseUrl
, projectPath
, or dllPath
must be provided. URL sources must be absolute HTTP(S) URLs. Project and DLL sources must have the expected extension and must already exist on disk. Prefer this factory for CLI and host-facing option binding so source-specific parameters do not accidentally affect the wrong source kind.
Default ICommandExecutor
implementation backed by CliWrap buffered command execution.
This implementation models launch failures as ProcessResult
instances instead of throwing so resolver code can treat command execution as data and decide whether to fall back or raise a richer exception. If the command is canceled after launch starts, cancellation is propagated. CliWrap owns child-process termination for the canceled command task.
Task<ProcessResult> ExecuteCommandAsync(string fileName, IReadOnlyList<string> args, string workingDirectory, CancellationToken cancellationToken)
Executes a child process, captures its output streams, and returns the resulting ProcessResult
.
fileName
args
workingDirectory
cancellationToken
A ProcessResult
whose fields contain the exit code, stdout, and stderr on success, or a synthetic failure result when the process cannot be started or configured.
OperationCanceledException
The method intentionally returns ProcessResult
for launch/setup failures so callers can preserve command context in their own diagnostics. Arguments are passed to CliWrap as ordered tokens, not as a shell command string. Standard output and standard error remain separate unbounded buffers to preserve the previous Process
contract for this CLI slice.
Discovers and rewrites exporter-managed references in HTML and CSS bodies.
The processor intentionally separates semantic discovery from output rewriting. HTML discovery uses AngleSharp so valid browser markup such as unquoted attributes and case-insensitive element names is traversed through a parser. Rewrite operations continue to operate on the original source text and replace only the attribute or CSS token value that resolves to an emitted artifact, preserving document formatting, comments, casing, and unrelated attributes.
IReadOnlyList<ExportReference> ExtractReferences(string content, string currentRoute, bool htmlScope)
Extracts exporter-managed internal references from HTML or CSS content.
content
currentRoute
content
.htmlScope
true
for HTML documents; false
for standalone CSS bodies.Managed references with URL provenance. External, hash-only, data, JavaScript, mailto, and malformed values are filtered out.
string RewriteManagedReferences(string content, string currentRoute, bool htmlScope, Func<ExportReference, string?> resolveArtifactUrl)
Rewrites managed references to their emitted artifact URLs while preserving the surrounding source text.
content
currentRoute
content
.htmlScope
true
for HTML documents; false
for standalone CSS bodies.resolveArtifactUrl
null
when unresolved.The rewritten content. Unresolved, external, malformed, and unsupported references remain unchanged.
string ResolveRelativeUrl(string baseRoute, string url)
Resolves a potentially relative URL against a base route.
baseRoute
url
A root-relative URL when resolution succeeds; otherwise the original url
.
bool TrySplitManagedUrl(string rawRef, out string path, out string query, out string fragment)
Splits an exporter-managed root-relative URL into path, query, and fragment parts.
rawRef
path
query
fragment
true
when rawRef
is a valid exporter-managed URL.
void AddRazorWireSectionCopyAutoloadReferences(ICollection<ExportReference> references, IDocument document, string currentRoute, IReadOnlyDictionary<HtmlAttributeLookupKey, HtmlAttributeSpan> attributeLookup)
Adds the section-copy runtime script reference required by lazy RazorWire section-copy markup.
references
document
currentRoute
attributeLookup
The export pipeline needs a concrete ExportReferenceKind.ScriptSrc
reference even when a host relies on the <rw:scripts />
lazy detector instead of rendering section-copy.js
eagerly. This method first skips documents that already reference /razorwire/section-copy.js
in any script src
, using case-insensitive matching so versioned or path-base-qualified runtime URLs are treated as already materialized. It then looks for data-rw-section-copy
or data-rw-section-copy-target
markers, prefers the autoload script's own encoded source
assignment when present, and otherwise injects the default RazorWire runtime path with HTML provenance from the first marker.
void AddRazorWireFormInteractionsAutoloadReferences(ICollection<ExportReference> references, IDocument document, string currentRoute, IReadOnlyDictionary<HtmlAttributeLookupKey, HtmlAttributeSpan> attributeLookup)
Adds a synthetic static-export reference for the RazorWire form-interactions runtime when lazy markup requires it.
references
document
currentRoute
attributeLookup
The exporter first honors any explicit form-interactions.js
script tag, then prefers the inline autoload block's source
value when the lazy detector is present, and finally falls back to the managed package path. This order preserves host-owned eager loading while still materializing the split runtime for exported pages that rely on <rw:scripts />
lazy loading. The scan is intentionally limited to top-level form-toggle and form-collection markers; malformed collection templates remain runtime diagnostics rather than export blockers.
A terminal/CLI module for RazorWire providing static site export capabilities.
void ConfigureServices(StartupContext context, IServiceCollection services)
Configures services needed for the CLI, including the export pipeline and command-owned logging defaults.
context
services
void ConfigureHostBeforeServices(StartupContext context, IHostBuilder builder)
Executes pre-service host configuration; currently no implementation is required.
context
builder
void ConfigureHostAfterServices(StartupContext context, IHostBuilder builder)
Executes post-service host configuration; currently no implementation is required.
context
builder
void RegisterDependentModules(ModuleDependencyBuilder builder)
Registers dependencies for this module; currently no implementation is required.
builder
Describes split-origin hybrid export behavior.
bool TryNormalizeOrigin(string? origin, out string? normalizedOrigin)
Normalizes an optional origin string for hybrid export.
origin
normalizedOrigin
true
when the origin is blank or valid; otherwise false
.
ExportHybridOptions Default { get; }
Source
Gets a new default instance that preserves existing export behavior.
A fresh mutable instance is returned on each access so caller changes do not affect later default callers.
string? LiveOrigin { get; set; }
Source
Gets or sets the live origin used by RazorWire-managed dynamic references.
RazorWireHybridCredentialsMode CredentialsMode { get; set; }
Source
Gets or sets the credential behavior used for RazorWire-managed live references.
bool HasLiveOrigin { get; }
Source
Gets a value indicating whether split-origin live references are enabled.
bool IncludesCredentials { get; }
Source
Gets a value indicating whether managed live calls should include credentials.
Represents exporter-domain validation failures that prevent safe export output from being produced.
The export engine throws this exception without depending on CLI infrastructure. Command handlers should translate it into the appropriate command-line failure type for their host.
IReadOnlyList<ExportDiagnostic> Diagnostics { get; }
Source
Gets the diagnostics that describe why export validation failed.
Validates static export text artifacts for RazorWire auth projection leaks before bytes are published.
Task WriteTextArtifactAsync(string outputPath, string artifactPath, string artifactKind, string? route, string contents, Encoding? encoding, CancellationToken cancellationToken)
Validates and writes a generated text artifact, failing before opening the destination when auth content is unsafe.
Use this for string materialization paths such as route manifests, redirects, and release manifests. The route and artifact path are diagnostic context only; the output path boundary is still enforced by ExportOutputPathGuards
after the auth audit passes.
Task WriteTextArtifactBytesAsync(string outputPath, string artifactPath, string artifactKind, string? route, byte[] contents, Encoding? declaredEncoding, CancellationToken cancellationToken)
Validates and writes generated text bytes without changing their original encoding.
The byte payload is decoded for audit using the declared charset when available, then UTF-8 and Windows-1252 fallbacks. The exact input bytes are written only after all decoded representations pass, which keeps legacy encoded text stable while preserving fail-closed auth leak detection.
void ValidateTextArtifact(string contents, string artifactKind, string? route, string? artifactPath = null)
Validates rendered text for forbidden static auth projection markers.
The audit checks raw text plus HTML-decoded and JSON-unescaped forms so escaped private auth markers cannot hide in search payloads, manifests, JavaScript strings, or copied text extras. Diagnostics intentionally name the reason label and artifact kind without echoing sensitive policy, subject, claim, or persona values.
void ValidateTextArtifactBytes(byte[] contents, string artifactKind, string? route, string? artifactPath = null, Encoding? declaredEncoding = null)
Validates text bytes for forbidden static auth projection markers.
Pass a declared encoding from the HTTP charset when known. Unknown or unsupported charsets are ignored by ResolveDeclaredEncoding(string?)
so the UTF-8 and Windows-1252 audit fallbacks still run.
bool IsTextArtifact(string? contentType, string artifactPath)
Classifies response artifacts that are known text from content type or filename.
This method follows explicit HTTP and extension signals. Use ShouldAuditLocalTextArtifact(string)
for final inventory or local-copy scans where extensionless artifacts are also text candidates.
bool ShouldAuditLocalTextArtifact(string artifactPath)
Determines whether a local artifact path should be audited as generated text.
Final inventory treats extensionless files as auditable because static routes commonly materialize without file extensions. Callers must pass the concrete output path that will be written or scanned.
Encoding? ResolveDeclaredEncoding(string? charset)
Resolves an optional HTTP charset for byte-preserving artifact audits.
Returns null
for missing, invalid, or unsupported charsets so audit callers can continue with default decoding fallbacks instead of treating a bad charset as permission to skip validation.
Describes a redirect alias registration that should be materialized after the canonical export route is proven.
AliasRoute
/docs/examples/app/README.md.html
. CanonicalRoute
Redirect registrations are part of the export graph so validation can reject collisions, missing canonical pages, and provider-specific output conflicts before files are overwritten. They are not crawler seeds. The selected ExportRedirectStrategy
decides whether registrations become generic HTML fallback files or provider redirect rules such as Netlify's root _redirects
file.
Describes one CDN export validation problem with a stable code and actionable context.
Diagnostics are immutable value objects suitable for command-line output, logs, and structured inspection through ExportValidationException.Diagnostics
. The public contract exposes the stable code, plain-text message, and route context. Internal exporter code may attach the discovered reference that produced the diagnostic for de-duplication and richer validation decisions.
string Code { get; }
Source
Gets the short machine-readable diagnostic identifier.
string Message { get; }
Source
Gets the human-readable plain-text validation message.
string Route { get; }
Source
Gets the root-relative route or export context where the diagnostic was produced.
ExportReference? Reference { get; }
Source
Gets the optional exporter-managed reference that produced the diagnostic.
This value is internal because it is tied to crawl provenance rather than the public validation exception contract. It is null
for diagnostics that describe route-level failures instead of a specific markup or CSS reference.
Describes a validated export source and the launch options needed to make it crawlable.
SourceKind
SourceValue
Framework
SourceKind
is ExportSourceKind.Project
. AppArgs
NoBuild
Create instances through ExportSourceRequestFactory
when processing CLI input so source selection, source-specific options, and file existence checks remain consistent. Direct construction is intended for tests and host integrations that already validated their inputs.
Represents a started or startable external target application process.
The wrapper is a supervised long-running process boundary for export targets, not a general command runner. Implementations raise non-empty output lines as they arrive, raise Exited
after observable output has drained when possible, and treat disposal as best-effort process-tree cleanup.
void Start()
Starts the process and begins asynchronous output capture.
InvalidOperationException
ObjectDisposedException
Calling Start
more than once is invalid. Startup failures that occur after the command task is created are surfaced through ErrorLineReceived
followed by Exited
so callers waiting for a listening URL can report the underlying launch failure instead of a generic readiness timeout.
ValueTask DisposeAsync()
Performs best-effort asynchronous cleanup of the started target process.
A task that completes after cleanup work finishes.
HasExited
when startup completed.Exited
is raised only after the command task completes and output pipes have drained when possible.InvalidOperationException
, timeout-driven OperationCanceledException
, ObjectDisposedException
, and recoverable command-task exceptions such as CliWrapException
, Win32Exception
, or NotSupportedException
as part of best-effort disposal.bool HasExited { get; }
Source
Gets a value indicating whether the process has exited.
This value reflects the wrapper lifecycle, not only operating-system process liveness. It returns true
before Start
is called, while completion is being observed, and after disposal. It returns false
only while the target app is actively running. Callers that need to distinguish not-started, running, completing, and disposed states should use the surrounding lifecycle ordering instead of treating this as a simple liveness probe.
Creates ITargetAppProcess
instances for launch specifications.
ITargetAppProcess Create(ProcessLaunchSpec spec)
Creates a new process wrapper for the provided launch spec.
spec
A process wrapper ready to start.
Default ITargetAppProcessFactory
implementation.
Optional process-operation overrides for TargetAppProcess
tests.
These hooks exist so tests can force cleanup branches such as unsupported kill operations, synthetic exit states, or timeout handling without reflection or fragile platform-dependent child processes.
Action<TargetAppProcess>? StartOverride { get; init; }
Source
Gets or sets an optional start override used in place of Process.Start()
and output-reader setup.
Tests can use this to deterministically raise ITargetAppProcess.Exited
and ITargetAppProcess.OutputLineReceived
in a controlled order without depending on operating-system process timing.
Func<Process, bool>? HasExitedOverride { get; init; }
Source
Gets or sets an optional exit-state override used in place of Process.HasExited
.
Action<Process>? KillProcessOverride { get; init; }
Source
Gets or sets an optional kill override used in place of Process.Kill(bool)
.
Func<Process, CancellationToken, Task>? WaitForExitAsyncOverride { get; init; }
Source
Gets or sets an optional asynchronous wait override used in place of Process.WaitForExitAsync(CancellationToken)
.
Action<Process>? WaitForExitOverride { get; init; }
Source
Gets or sets an optional synchronous wait override used in place of Process.WaitForExit()
.
Represents a crawlable export source URL and any target process owned by the resolver.
ValueTask DisposeAsync()
Disposes any target application process launched while resolving the source.
Disposing a URL-backed source is a no-op because URL sources are owned by the caller. Disposing a launched source delegates to the owned process once so the temporary app host is stopped after crawling or after a failed export.
string BaseUrl { get; }
Source
Gets the resolved base URL that the export engine should crawl.
The value is produced by ExportSourceResolver
and is expected to be an absolute HTTP(S) URL. It is stored exactly as provided except that blank values are rejected by the constructor.
Provides the RazorWire CLI entry surface with the command-first console behavior required for public tool flows.
Task RunAsync(string[] args, Action<ConsoleOptions>? configureOptions = null)
Runs the RazorWire CLI directly through the command service while still allowing targeted startup customization.
args
configureOptions
A task that completes when the CLI command finishes running.
Public command-line tool entry points need predictable command output for help, validation errors, and export progress. Running the command service directly keeps those flows independent from the Generic Host lifecycle while preserving AppSurface's command registration, dependency injection, and unknown-option suggestions.
Provides context and state for an export operation, including configuration and crawl progress.
void EnableReleaseArchiveManifest()
Enables generation of the AppSurface Docs release archive manifest after final export materialization.
Host integrations should call this only when the output tree is intended to satisfy the AppSurface Docs exact release archive contract. The exporter then writes .appsurface-docs-release-manifest.json
and populates ReleaseArchiveManifest
with the digest that should be pinned in trusted catalog configuration.
void AddRedirectAlias(string aliasRoute, string canonicalRoute)
Registers a route alias that should redirect to an already-exported canonical route.
aliasRoute
canonicalRoute
ArgumentException
This API is intended for hosts that know route aliases before crawling starts. The export engine validates registered aliases before materialization so collisions and missing canonical artifacts fail early. The selected RedirectStrategy
controls whether aliases become HTML fallback artifacts or provider redirect rules, so source-shaped aliases do not become duplicate public pages.
void AddRedirectArtifact(string aliasRoute, string canonicalRoute)
Registers a route whose static output should redirect to an already-exported canonical route.
aliasRoute
canonicalRoute
ArgumentException
This compatibility wrapper preserves the original artifact-oriented API name. New host integrations should call AddRedirectAlias
so code describes the route relationship instead of the selected materialization strategy.
void AddSeedRoute(string seedRoute)
Registers an additional route for the export crawler to visit before validation.
seedRoute
ArgumentException
seedRoute
is blank.Use this when a host already knows its public route graph. The export engine validates, normalizes, and de-duplicates registered routes during the seed queue phase. Query strings and fragments follow the same rules as other seed sources, but host-specific route manifests should generally register clean canonical paths.
void AddDeploymentExtra(string sourcePath, string publishPath)
Registers a deployment-owned file that should be copied into the publish root after export validation succeeds.
sourcePath
publishPath
/CNAME
or /.well-known/security.txt
. This is not an app route, seed route, URL, or directory path. ExportValidationException
RWEXPORT007
diagnostics when the source file or publish path violates the deployment extras contract.
Deployment extras are explicit single files for host metadata such as GitHub Pages CNAME
files. They are validated with the same rules as CLI manifest entries, never overwrite generated exporter output, and are incompatible with exact AppSurface Docs release archive exports. Raw provider-owned files such as /_redirects
and /_headers
are reserved so future structured provider support can remain exporter-owned.
void AddDeploymentExtra(ExportDeploymentExtra extra)
Registers a prevalidated deployment extra for publish-root materialization.
extra
ExportDeploymentExtra.SourcePath
and root-relative ExportDeploymentExtra.PublishPath
. ArgumentNullException
extra
is null
.ExportValidationException
RWEXPORT007
diagnostics when a case-insensitive duplicate publish path is registered.
This overload exists for internal call paths that already produced an ExportDeploymentExtra
instance. It still enforces case-insensitive publish-path uniqueness using StringComparison.OrdinalIgnoreCase
; callers should not register duplicate publish-root targets.
string OutputPath { get; }
Source
Gets the path where exported files will be saved.
string? SeedRoutesPath { get; }
Source
Gets the optional path to a seed routes file.
When this path is set, file-based seeds take precedence over InitialSeedRoutes
so existing CLI callers keep their explicit seed-file behavior.
IReadOnlyList<string> InitialSeedRoutes { get; }
Source
Gets optional in-memory seed routes used when SeedRoutesPath
is not configured.
Hosts that already know their default routes can pass them directly instead of writing a temporary seed file. Values are validated and normalized by the export engine using the same rules as file-based seeds. When no valid in-memory seed remains, the engine falls back to the root route ( /
).
IReadOnlyList<string> AdditionalSeedRoutes { get; }
Source
Gets host-registered seed routes that should be crawled in addition to configured seed-file or in-memory seeds.
Host-specific export integrations can register routes discovered from their own route graph before crawling starts. These routes are validated by the export engine with the same normalization rules as configured seeds. They do not replace SeedRoutesPath
or InitialSeedRoutes
; they make known public routes explicit so exports do not depend on every page being linked from the initial crawl roots.
IReadOnlyList<ExportDeploymentExtra> DeploymentExtras { get; }
Source
Gets deployment-owned files that should be copied into the publish root after exporter-owned artifacts are proven.
The public registration surface is AddDeploymentExtra(string,string)
. This internal view lets the export engine validate collisions against generated artifacts and then materialize the files. Entries are already normalized to absolute source file paths and root-relative publish paths.
string BaseUrl { get; }
Source
Gets the base URL of the source application being exported.
ExportMode Mode { get; }
Source
Gets the export mode that controls URL rewriting and validation behavior.
ExportMode.Cdn
is the default and rewrites exporter-managed internal URLs to emitted artifacts while validating that those managed dependencies can be served by a static host. ExportMode.Hybrid
preserves application-style internal URLs for server-backed deployments that still provide routing and dynamic behavior. CDN validation and rewriting only apply to exporter-managed URLs discovered in markup and CSS; unmanaged external, JavaScript, mailto, hash-only, and data URLs are intentionally ignored rather than validated or rewritten.
ExportRedirectStrategy RedirectStrategy { get; }
Source
Gets the strategy used to materialize registered redirect aliases.
ExportRedirectStrategy.Html
is the default for generic static hosts and writes tiny alias HTML files after canonical pages are materialized. ExportRedirectStrategy.Netlify
writes one root _redirects
file with exact provider rules and is intended for CDN exports published to Netlify or a compatible CDN. Netlify validation operates on the serialized provider rule paths, so duplicate exact rules are de-duplicated while self-redirects and same-source/different-target rules fail before files are written.
ExportHybridOptions Hybrid { get; }
Source
Gets split-origin hybrid export options.
The context stores normalized options. ExportHybridOptions.LiveOrigin
is either null
or an absolute HTTP(S) origin with no path, query, fragment, or userinfo.
string? PublicOrigin { get; }
Source
Gets the optional public origin used when export rewrites same-origin metadata for the published static host.
This origin does not change the crawl source or application links. It is intended for metadata such as canonical links that must identify the public static host even though the exporter crawls a loopback or private source URL. When set, it is normalized to an absolute HTTP(S) origin with no path, query, fragment, or userinfo.
HashSet<string> Visited { get; }
Source
Gets the set of URLs that have already been visited during the crawl.
Queue<string> Queue { get; }
Source
Gets the queue of URLs pending processing.
HashSet<string> Enqueued { get; }
Source
Gets the normalized routes that have already been scheduled for crawl processing.
This set mirrors Queue
membership over the lifetime of an export so duplicate reference discovery can perform O(1) scheduling checks without scanning the pending queue. Routes remain in this set after dequeue because Visited
and RouteOutcomes
record their terminal crawl state.
Dictionary<string, ExportRouteOutcome> RouteOutcomes { get; }
Source
Gets route fetch outcomes keyed by normalized root-relative route.
List<ExportReference> References { get; }
Source
Gets every managed internal reference discovered during the crawl, including duplicate provenance.
List<ExportDiagnostic> Diagnostics { get; }
Source
Gets CDN validation diagnostics produced for this export.
Dictionary<string, string> ArtifactUrls { get; }
Source
Gets static-host artifact URLs keyed by normalized route.
Dictionary<string, string> PartialArtifactUrls { get; }
Source
Gets generated AppSurface Docs partial artifact URLs keyed by their source full-page route.
List<ExportRedirectArtifact> RedirectArtifacts { get; }
Source
Gets redirect aliases registered by host-specific exporters.
ReleaseArchiveManifestSummary? ReleaseArchiveManifest { get; set; }
Source
Gets the release archive manifest summary written after export materialization completes.
The summary is populated by ExportEngine
after final static files have been written when ReleaseArchiveManifestEnabled
is true
. Command surfaces use it to print the catalog digest operators should pin with releaseManifestSha256
.
bool ReleaseArchiveManifestEnabled { get; set; }
Source
Gets a value indicating whether this export should emit an AppSurface Docs release archive manifest.
Generic RazorWire exports leave this disabled because arbitrary static sites may need valid hidden web paths such as .well-known
. AppSurface Docs exact-release export enables it after writing the frozen route manifest so operators receive a catalog-pinned integrity digest for that release archive.
Describes one exporter-managed internal URL discovered while crawling HTML or CSS.
SourceRoute
Kind
Role
ExportReferenceKind
and ExportReferenceRole
: anchors are page routes, Turbo Frames are live routes, and scripts, images, and CSS URLs are static assets. Link references are classified from rel
tokens, optional as
values, and the resolved path extension. RawValue
ResolvedUrl
RawValue
.Path
Query
Fragment
Provenance
null
value means the caller did not preserve element, attribute, or CSS-token context. LinkMetadata
link
attributes used to explain preload, modulepreload, stylesheet, icon, canonical, and DNS-hint classification. It is populated only for ExportReferenceKind.LinkHref
references discovered from HTML link href
elements; callers should expect null
for anchors, scripts, images, and CSS URLs.
RawValue
is the exact value found in markup or CSS after outer attribute/token whitespace is trimmed. ResolvedUrl
is the root-relative URL after resolving relative references against SourceRoute
. Path
is the query-free route used for fetch de-duplication, while Query
and Fragment
preserve the original URL shape for CDN validation and rewriting decisions. Role
records validation policy derived by ExportReferenceProcessor
from Kind
, ExportReferenceKind.LinkHref
metadata, and static-asset path heuristics. Provenance
records the HTML element/attribute or CSS token that produced the reference so validation diagnostics can explain what to fix.
bool RequiresStaticMaterialization(ExportMode mode)
Determines whether the selected export mode requires this reference to materialize as an exported static asset.
mode
true
when missing materialization should fail validation.
CDN and hybrid exports both rewrite static assets to materialized files, but page routes and live routes are validated through their route outcomes instead. This method intentionally keys off Role
so supported link
hints that resolve to page routes do not become false missing-asset diagnostics. It differs from IsAsset
because a reference can be classified as a static asset while an export mode that does not materialize static output would not require missing-asset validation.
bool IsAsset { get; }
Source
Gets a value indicating whether the reference points at a browser-delivered static asset.
Page and live-route references can still be valid internal URLs, but they are not copied into the static asset output set and should not be treated as required files during hybrid validation. This property is independent of export mode: it is true
whenever Role
is ExportReferenceRole.StaticAsset
.
A command for exporting a RazorWire site to a static directory.
ValueTask ExecuteAsync(IConsole console)
Executes the export process for the RazorWire site to the configured output directory, validating options and writing progress to the console.
console
A ValueTask
that completes when the export operation finishes.
ValueTask ExecuteAsync(IConsole console, CancellationToken cancellationToken)
Executes the export process using an explicit cancellation token.
console
cancellationToken
A ValueTask
that completes when the export operation finishes.
string OutputPath { get; set; }
Source
Gets or sets the path to the directory where the exported site will be written. Defaults to "dist"
.
string? SeedRoutesPath { get; set; }
Source
Gets or sets an optional path to a plain-text file containing one initial seed route per line.
This property is bound from the -r|--seeds
command option. When it is null
or empty, the exporter starts from the root route ( /
). When it points to a file, the exporter reads each line, accepts root-relative routes and absolute HTTP(S) URLs, strips query strings and fragments during normalization, and skips invalid, external, hash-only, JavaScript, or mailto entries. If the file is missing or unreadable, export fails and returns a non-zero CLI exit code. If the file is readable but contains no valid routes, the exporter logs a warning and falls back to the root route.
string? PublishRootExtrasPath { get; set; }
Source
Gets or sets an optional path to a publish-root deployment extras manifest.
The manifest declares explicit deployment-owned files, such as CNAME
, that should be copied into the publish root after export proves they do not collide with generated output. This is distinct from SeedRoutesPath
, which contains routes to crawl, not local files.
ExportMode Mode { get; set; }
Source
Gets or sets the export mode that controls whether output is rewritten for static CDN hosting.
ExportMode.Cdn
is the default and emits fully static output for plain static hosts. Use ExportMode.Hybrid
when the exported directory will still be served behind application-style routing that can resolve extensionless URLs.
string? LiveOrigin { get; set; }
Source
Gets or sets the live origin used for RazorWire-managed live references in hybrid exports.
This option is optional and defaults to same-origin behavior. When set, the value must be an absolute http
or https
origin with no path, query string, fragment, or userinfo; for example, https://api.example.com
. In split-origin hybrid exports, RazorWire-owned streams, islands, and safe lazy anti-forgery forms are rewritten to this origin while ordinary application links stay app-relative. Prefer leaving this unset when the exported files and live app share an origin.
RazorWireHybridCredentialsMode HybridCredentials { get; set; }
Source
Gets or sets credential behavior for RazorWire-managed live references.
Defaults to RazorWireHybridCredentialsMode.Auto
. Auto includes credentials when LiveOrigin
is configured and omits them otherwise. Choose RazorWireHybridCredentialsMode.Include
when the live origin requires cookie-backed sessions across origins, and choose RazorWireHybridCredentialsMode.Omit
only for public, credential-free live surfaces. Lazy anti-forgery form refreshes require credentials when a split live origin is configured.
string? BaseUrl { get; set; }
Source
Gets or sets the base URL of a running application to crawl.
string? ProjectPath { get; set; }
Source
Gets or sets a path to a .csproj file to run and export.
string? DllPath { get; set; }
Source
Gets or sets a path to a .dll file to run and export.
string? Framework { get; set; }
Source
Gets or sets an optional target framework for project exports, required for multi-target projects.
string[] AppArgs { get; set; }
Source
Gets or sets app arguments forwarded to the launched target app. Repeat this option for each token.
bool NoBuild { get; set; }
Source
Gets or sets a value indicating whether project mode should skip build before launch.
Validates exporter-owned artifact paths before the CLI creates directories, opens files, or reads release archive entries.
The guard enforces the physical output-root boundary for generated artifacts. It rejects existing symlink, junction, or other reparse-point segments before the exporter follows them, while preserving ordinary non-empty output directories that contain regular files and directories only.
void ValidateOutputRootPath(string outputPath, string artifactKind, string? route, string operation)
Validates the output root and its existing ancestor segments without changing the filesystem.
outputPath
artifactKind
route
operation
void EnsureOutputRootReady(string outputPath, string artifactKind, string? route, string operation)
Ensures the output root can be used without following an existing reparse point.
outputPath
artifactKind
route
operation
void ValidateWritableArtifactPath(string outputPath, string artifactPath, string artifactKind, string? route, string operation)
Validates a generated artifact file path without changing the filesystem.
outputPath
artifactPath
artifactKind
route
operation
void EnsureWritableArtifactParent(string outputPath, string artifactPath, string artifactKind, string? route)
Creates the artifact parent directory only after validating all existing physical path segments.
outputPath
artifactPath
artifactKind
route
Task WriteTextArtifactAsync(string outputPath, string artifactPath, string artifactKind, string? route, string contents, Encoding? encoding, CancellationToken cancellationToken)
Writes a generated text artifact after guarding its parent creation and final open/write operation.
outputPath
artifactPath
artifactKind
route
contents
encoding
cancellationToken
FileStream OpenWritableArtifactStream(string outputPath, string artifactPath, string artifactKind, string? route)
Opens a generated file for writing after guarding its parent creation and final open operation.
outputPath
artifactPath
artifactKind
route
A write-only stream positioned at the start of the generated artifact.
void ValidateArchiveEntryPath(string outputPath, string entryPath, string operation)
Validates an existing release archive entry before traversal, metadata reads, hashing, or manifest inclusion.
outputPath
entryPath
operation
void ValidateExistingArtifactPath(string outputPath, string entryPath, string artifactKind, string operation)
Validates an existing exported artifact before traversal, metadata reads, hashing, or inventory scanning.
outputPath
entryPath
artifactKind
operation
Identifies the HTML or CSS surface where an export reference was found.
Selects how registered redirect aliases are materialized during static export.
Html
is the default because it works on generic static hosts, including GitHub Pages, by writing tiny alias HTML files that point at canonical artifacts. Netlify
writes a root _redirects
file for Netlify-compatible static hosting and does not emit alias HTML files. Netlify redirect output is intended for ExportMode.Cdn
exports only because the rules point at publish-root static routes. Netlify rules reserve the root _redirects
file, reject aliases that serialize to their own canonical target, and reject aliases that serialize to the same provider source path while pointing at different targets. New values must only be appended so existing serialized, configured, or logged enum values remain stable.
Identifies the kind of application source the exporter should crawl.
Numeric values are explicit because this public enum may be bound from configuration or serialized by host tooling. New values should be appended without changing existing values.
Selects how RazorWire export output should resolve internal application URLs.
The default selection is Cdn
. Choose Cdn
for output that will be served directly by a static host or CDN. Choose Hybrid
only when the exported files will still be hosted behind infrastructure that preserves application routing and dynamic server behavior. New values must only be appended so existing serialized or logged enum values remain stable.
Classifies how an exporter-managed reference participates in mode-specific validation policy.
The role is internal CLI metadata derived from the discovered reference kind plus nearby markup evidence such as link
rel
and as
attributes. It separates reference facts from validation policy so hybrid export can require browser-delivered static assets without treating page, live, or metadata references as CDN-owned assets.