bool HasPatterns { get; }
Source
Gets a value indicating whether any patterns were configured.
Resolves authored, source, and canonical AppSurface Docs paths against a harvested documentation corpus.
AppSurface Docs accepts paths from several authoring surfaces: browser routes, source-relative Markdown metadata, generated canonical paths, and route-prefixed links. This resolver is the shared source of truth for trimming route separators, ignoring lookup fragments when selecting candidate buckets, preserving exact fragment matches when available, and ranking fallback candidates when a fragment-specific page is not present.
DocPathResolver Create(IEnumerable<DocNode> docs)
Source
Builds a resolver for a docs snapshot.
docsThe harvested docs whose source and canonical paths should be resolvable.A resolver that can match source paths, canonical route paths, and route-relative variants.
DocNode? Resolve(string path)
Source
Resolves a path exactly as authored, using AppSurface Docs source and canonical matching rules.
pathThe authored source or canonical path to resolve.The best matching doc node, or null when no harvested doc matches.
DocNode? Resolve(string path, params string[] routeRootPaths)
Source
Resolves a path by preserving authored source-relative matches before stripping known docs route roots from browser-facing inputs.
pathThe authored or browser-facing path to resolve.routeRootPathsRoute roots, such as the configured live docs root and the stable /docs root.The best matching doc node, or null when neither route-relative variants nor the original path match.
string NormalizeLookupPath(string path)
Source
Normalizes a documentation path for lookup by trimming route separators and removing fragment anchors.
pathThe path to normalize.The normalized lookup path.
string NormalizeCanonicalPath(string path)
Source
Normalizes a documentation path for canonical comparison by trimming route separators while preserving fragments.
pathThe path to normalize.The normalized canonical path.
string? GetFragment(string path)
Source
Extracts a fragment from a documentation path after canonical normalization.
pathThe path that may contain a fragment anchor.The fragment without the leading #, or null when no non-empty fragment exists.
Defines the repository-relative path policy used by AppSurface Docs harvesters.
Implementations combine configured include or exclude rules with traversal pruning so harvesters can avoid enumerating large excluded directory trees. All paths passed to this contract must be repository-relative and normalized with forward slashes. Absolute paths, parent-directory traversal, and platform-specific separators should be normalized by the caller before evaluation.
AppSurfaceDocsHarvestPathDecision Evaluate(string relativePath, AppSurfaceDocsHarvestSourceKind sourceKind)
Source
Evaluates whether a repository-relative file path should be harvested for the specified source kind.
relativePathRepository-relative file path normalized with forward slashes.sourceKindThe harvester source kind requesting the decision.A decision containing the final inclusion result and diagnostic trace entries.
bool ShouldIncludeFilePath(string relativePath, AppSurfaceDocsHarvestSourceKind sourceKind)
Source
Determines whether a repository-relative file path should be included for the specified source kind.
relativePathRepository-relative file path normalized with forward slashes.sourceKindThe harvester source kind requesting the decision.true when the file should be read by the harvester; otherwise false.
bool ShouldPruneDirectory(string relativeDirectory, AppSurfaceDocsHarvestSourceKind sourceKind)
Source
Determines whether a repository-relative directory can be skipped before its descendants are enumerated.
relativeDirectoryRepository-relative directory path normalized with forward slashes.sourceKindThe harvester source kind requesting the decision.true when the directory subtree is excluded and can be pruned.
IEnumerable<string> EnumerateCandidateFiles(string rootPath, AppSurfaceDocsHarvestSourceKind sourceKind, string searchPattern, CancellationToken cancellationToken)
Source
Lazily enumerates candidate files below a repository root while applying directory pruning and skipping reparse points.
rootPathAbsolute repository root to traverse.sourceKindThe harvester source kind requesting candidates.searchPatternA file-system search pattern such as *.md.cancellationTokenA token observed before each directory is expanded.Absolute file paths that match searchPattern, are not below a pruned directory, and are not file-system reparse points.
File-level include checks are intentionally separate; callers must still pass returned files through ShouldIncludeFilePath after converting them back to repository-relative paths. Implementations skip reparse-point files and directories before yielding or descending so candidate traversal does not follow symlinks, junctions, or similar filesystem indirection outside the selected repository root.
Resolves environment-aware visibility for AppSurface Docs maintainer diagnostics routes.
bool ShouldShowChrome(AppSurfaceDocsOptions options, IHostEnvironment environment)
Source
Resolves whether the built-in sidebar should show route-inspector diagnostics chrome for the current host.
The default follows local-development ergonomics: Development hosts show route-inspector discovery by default, while non-development hosts must opt in explicitly. This method controls only docs chrome. It does not expose or authorize the route-inspector response; use IsRouteInspectorExposed for route response policy.
bool IsRouteInspectorExposed(AppSurfaceDocsOptions options, IHostEnvironment environment)
Source
Resolves whether the route-inspector controller routes should return responses for the current host.
The route inspector exposes route identity intended for local development and trusted operators. Missing diagnostics options fall back to AppSurfaceDocsHarvestHealthExposure.DevelopmentOnly. Setting the exposure to AppSurfaceDocsHarvestHealthExposure.Always only allows the controller response; hosts remain responsible for authentication, authorization, or network controls in production.
Evaluates normalized repository-relative paths against ordered harvest glob patterns.
Matching is case-insensitive. MatchFirst is order-dependent and returns the first configured pattern that matches the candidate. Directory helpers are conservative because pruning must never skip a file that a later allow pattern could include; callers should validate and normalize paths before invoking the matcher.
string? MatchFirst(string relativePath)
Source
Returns the first configured pattern that matches relativePath, or null when none match.
string? MatchDirectorySubtree(string relativeDirectory)
Source
Returns the first /** subtree pattern that matches relativeDirectory itself.
The matcher probes a sentinel child path under the directory. This keeps clear subtree excludes fast, but patterns that do not end in /** are ignored because they may describe only files or a narrower leaf match.
string? MatchDirectoryOrDescendantSubtree(string relativeDirectory)
Source
Returns the first /** pattern that could match the directory or one of its descendants.
string? MatchDirectoryOrDescendant(string relativeDirectory)
Source
Returns the first pattern that could match a file inside relativeDirectory or a descendant.
This is intentionally broader than MatchDirectoryOrDescendantSubtree so file-level allow globs such as .github/workflows/*.yml keep the containing default-excluded directories enumerable.
string? MatchFileInDirectoryOrDescendant(string relativeDirectory)
Source
Returns the first pattern that could match a file directly inside relativeDirectory or below one of its descendants.
Unlike MatchDirectoryOrDescendant, this helper is allowed to rule out nested directories when the remaining glob pattern can only match a file in the current directory, such as src/public*.js. Harvesters use it to avoid walking subtrees that cannot contribute matching files.
bool HasPatterns { get; }
Source
Gets a value indicating whether any patterns were configured.
Wraps a single configured glob pattern and exposes case-insensitive match helpers.
bool Matches(string relativePath)
Source
Returns whether relativePath matches Pattern.
bool MatchesDirectoryOrDescendant(string relativeDirectory)
Source
Returns whether Pattern can apply to the directory or any descendant path.
bool CouldMatchDirectoryOrDescendant(string relativeDirectory)
Source
Returns whether Pattern could match any file under relativeDirectory.
bool CouldMatchFileInDirectoryOrDescendant(string relativeDirectory)
Source
Returns whether Pattern could match a file under relativeDirectory.
string Pattern { get; }
Source
Gets the original configured pattern.
TextMateSharp-backed implementation of AppSurface Docs' internal code-block highlighting contract.
IGrammar? LoadGrammar(AppSurfaceDocsCodeLanguage language)
Source
Loads the TextMate grammar for a normalized language, returning null when no grammar exists.
languageThe normalized language descriptor.The loaded grammar, or null when the language has no TextMate scope.
void AppendTokens(StringBuilder builder, string line, IReadOnlyList<IToken> tokens)
Source
Appends a tokenized source line while preserving unclassified gaps and trailing text.
builderThe destination HTML builder.lineThe original source line.tokensTextMate tokens for the line.string? ResolveTokenClass(IReadOnlyList<string> scopes)
Source
Maps TextMate scopes to AppSurface Docs' small semantic token vocabulary.
scopesThe scopes attached to a TextMate token.The AppSurface Docs token modifier, or null for unstyled scopes.
Authorizes the AppSurface Docs harvest progress stream with harvest visibility and host-owned stream policy.
ValueTask<bool> CanSubscribeAsync(HttpContext context, string channel)
Source
Determines whether the current request can subscribe to the requested RazorWire channel.
contextThe current HTTP context.channelThe requested channel name.true when channel passes harvest visibility checks and optional delegated authorization; otherwise false.
AppSurfaceDocsStreamAuthorization.IsHarvestProgressChannel(string?) identifies the harvest progress stream so only that channel is tied to AppSurfaceDocsHarvestHealthVisibility.AreRoutesExposed(AppSurfaceDocsOptions, IHostEnvironment). The method has no shared mutable state beyond reading constructor dependencies, but the delegated authorizer may perform asynchronous policy checks.
Lets an optional harvester tell AppSurface Docs whether it should participate in the current source snapshot.
Disabled optional harvesters are omitted from aggregate harvest-health accounting so a deliberately disabled feature cannot convert an otherwise all-failed snapshot into a degraded one. Direct calls to the harvester may still return an empty document set for compatibility.
bool IsEnabled { get; }
Source
Gets a value indicating whether the harvester should run and count toward harvest-health totals.
Owns Markdown code-fence language normalization, safe CSS class suffixes, and TextMateSharp lookup ids.
AppSurfaceDocsCodeLanguage Normalize(string? language)
Source
Normalizes an authored language token into AppSurface Docs' stable language contract.
languageThe raw first info-string token.A safe language descriptor for rendering and TextMate lookup.
string CreateSafeClassSlug(string value)
Source
Converts arbitrary language input into a CSS-safe lowercase ASCII slug.
valueThe value to slug.A lowercase slug containing only ASCII letters, digits, and hyphens.
Provides parser-input admission for source harvesters that need to bound bytes before decoding or parsing.
The helper reads at most maxFileSizeBytes + 1 bytes so oversized files can be rejected without loading the whole source into memory. Seekable streams still report their full file length in diagnostics when available.
Task<AppSurfaceDocsParserInputReadResult> ReadUtf8SourceAsync(string filePath, string relativePath, long maxFileSizeBytes, string configurationKey, string diagnosticCode, string harvesterType, string sourceKindLabel, string generatedSourceGuidance, CancellationToken cancellationToken)
Source
Reads a source file through a positive byte budget and decodes it for parser consumption when it is within budget.
filePathAbsolute or rooted file path to open for the bounded read.relativePathRepository-relative display path used in emitted diagnostics.maxFileSizeBytesMaximum number of bytes allowed before decoding; must be greater than zero.configurationKeyConfiguration key shown in diagnostics so operators know which limit rejected the file.diagnosticCodeDiagnostic code to emit when the file exceeds maxFileSizeBytes.harvesterTypeHarvester identifier recorded on emitted diagnostics.sourceKindLabelHuman-readable source kind, such as C#, included in diagnostic problem text.generatedSourceGuidanceRecovery guidance appended to oversized-file diagnostics.cancellationTokenToken observed while reading and decoding the source.A read result with AppSurfaceDocsParserInputReadResult.Included set when the source is within budget and decoded, or skipped with a warning diagnostic when the budget is exceeded.
The method throws ArgumentOutOfRangeException when maxFileSizeBytes is zero or negative. Files are decoded with StreamReader using UTF-8 as the default encoding and BOM detection enabled, so a recognized BOM is consumed instead of becoming source text. Oversized diagnostics use diagnosticCode, configurationKey, harvesterType, sourceKindLabel, and generatedSourceGuidance to describe the skipped file and recovery path.
DocHarvestDiagnostic CreateFileTooLargeDiagnostic(string diagnosticCode, string harvesterType, string relativePath, long observedSizeBytes, long maxFileSizeBytes, string configurationKey, string sourceKindLabel, string generatedSourceGuidance)
Source
Creates the warning diagnostic emitted when a parser-input budget rejects a source file.
diagnosticCodeDiagnostic code that identifies the source-specific oversized-file condition.harvesterTypeHarvester identifier attached to the diagnostic.relativePathRepository-relative source path shown to operators.observedSizeBytesObserved byte size, preferably the full file length when the stream is seekable.maxFileSizeBytesConfigured positive byte limit that rejected the file.configurationKeyConfiguration key that controls maxFileSizeBytes.sourceKindLabelHuman-readable source kind included in the diagnostic problem.generatedSourceGuidanceRecovery guidance for generated or intentionally large source.A non-strict warning diagnostic suitable for harvest health output.
Represents the outcome of a bounded parser-input read.
Includedtrue when the source was within budget and Source contains decoded text; false when the source was skipped and Diagnostic explains why.SourceDecoded source text for included files, otherwise null.DiagnosticWarning diagnostic for skipped files, otherwise null.Callers should branch on Included before using Source. Skipped results are intended to let harvesters continue processing sibling files while surfacing a visible diagnostic.
AppSurfaceDocsParserInputReadResult Read(string source)
Source
Creates an included result for decoded source that stayed within the byte budget.
sourceDecoded source text ready for parser consumption.An included parser-input result with no diagnostic.
AppSurfaceDocsParserInputReadResult Skipped(DocHarvestDiagnostic diagnostic)
Source
Creates a skipped result for a source file rejected before decoding or parsing.
diagnosticDiagnostic that explains why the source was skipped and how to recover.A skipped parser-input result with no decoded source.
Represents one locale-prefixed public route candidate derived from a localized document variant.
SourcePathNormalized source path for the localized variant.LocaleConfigured locale code associated with the variant.TranslationKeyStable translation identity shared by localized variants.PublicRoutePathLocale-prefixed browser route candidate for the variant.Candidates are produced only for variants that have a public route and a configured locale route prefix. Source paths and public route paths are normalized relative docs paths, not absolute URLs.
Owns the route identity contract for one cached AppSurface Docs snapshot.
The catalog deliberately separates source identity from public route identity: source path -> internal lookup and authoring provenance public route path -> browser-facing canonical URL redirect alias -> declared or Markdown source-shaped URL that redirects to public route Controllers render only public canonical winners. Declared aliases and Markdown source-shaped paths for public winners redirect to the canonical route, while non-Markdown source paths, collision losers, and reserved routes stay non-public. Link builders can still resolve source paths so authored Markdown stays source-friendly without rendering source-shaped URLs into the reader-facing surface.
AppSurfaceDocsRouteManifest BuildRouteManifest()
Source
Builds a snapshot-local manifest of public canonical routes and redirect aliases for export consumers.
A deterministic route manifest for the current catalog snapshot.
Use this when a downstream component needs a deterministic read model of canonical winners plus alias metadata. Prefer ResolvePublicRoute(string) for live request-time route decisions. The returned entries are ordered from _publicIdentityByRoutePath by public route path, then source path, using ordinal-ignore-case comparison so export output is stable across runs. Diagnostics start as a snapshot of Diagnostics and are passed through BuildRouteManifestEntry so implicit recovery-alias collisions discovered during manifest materialization are included in the returned AppSurfaceDocsRouteManifest. Pitfall: this method does not mutate catalog state, and callers should not rely on reference equality with external diagnostic collections or assume manifest-only diagnostics were computed before this call.
IReadOnlyList<LocalizedDocRouteCandidate> BuildLocalizedRouteCandidates(LocalizedDocsGraph graph, AppSurfaceDocsLocalizationOptions options)
Source
Builds locale-prefixed route candidates from a localized document graph.
graphLocalized graph built for the same docs snapshot.optionsLocalization options used to resolve locale route prefixes.Sorted locale-prefixed candidates suitable for later route registration slices.
Returns an empty list when the graph is disabled. Variants are skipped when they have no public route, or when their locale no longer maps to a configured route prefix. The method is snapshot-local and does not mutate catalog state.
Describes the rule outcome that caused a harvest path decision or trace entry.
Verifies catalog-pinned AppSurface Docs release archive manifests.
The verifier treats the version catalog as trusted host configuration. It proves that the local exact-version tree matches the digest pinned by that catalog; it does not prove who built the archive or replace future signed attestation support.
bool TryVerify(string exactTreePath, string expectedManifestSha256, out AppSurfaceDocsVerifiedReleaseArchive? archive, out AppSurfaceDocsArchiveVerificationFailure? failure)
Source
Attempts to verify an exact-version tree against the manifest digest pinned in the version catalog.
exactTreePathExact-version export root.expectedManifestSha256Catalog-pinned manifest digest.archiveVerified archive metadata when verification succeeds.failureVerification failure when verification fails.true when every required archive integrity check passes.
bool TryVerify(string exactTreePath, string expectedManifestSha256, AppSurfaceDocsReleaseArchiveFileSystem fileSystem, out AppSurfaceDocsVerifiedReleaseArchive? archive, out AppSurfaceDocsArchiveVerificationFailure? failure)
Source
Attempts to verify an exact-version tree using the supplied filesystem adapter.
exactTreePathExact-version export root.expectedManifestSha256Catalog-pinned manifest digest.fileSystemFilesystem adapter used for verification reads.archiveVerified archive metadata when verification succeeds.failureVerification failure when verification fails.true when every required archive integrity check passes.
bool FileMatches(IFileInfo fileInfo, AppSurfaceDocsReleaseArchiveFile expectedFile)
Source
Recomputes an already-verified file digest from an IFileInfo before serving active content.
fileInfoResolved file to check.expectedFileManifest entry to compare against.true when length and SHA-256 still match the manifest entry.
Filesystem adapter used by release archive verification.
The production adapter reads the physical release tree. Tests use custom adapters to exercise portable failure branches such as unreadable files without depending on platform-specific chmod behavior.
StringComparer GetPathComparer(string rootPath)
Source
Gets the comparer used by the physical filesystem when resolving paths beneath an archive root.
rootPathExisting exact release tree whose filesystem behavior is required.The comparer matching the root's physical filesystem casing rules.
StringComparer ResolvePhysicalPathComparer(string rootPath, Func<string, bool> fileExists, Func<string, IEnumerable<string>> enumerateFileSystemEntries)
Source
Resolves physical filesystem casing behavior without writing probe files into an immutable archive.
rootPathExisting exact release tree.fileExistsFile existence operation used for the read-only case-variant probe.enumerateFileSystemEntriesFilesystem enumeration used to reject ambiguous case-variant siblings.An ordinal comparer matching the archive root's case behavior.
bool FileExists(string path)
Source
Returns whether the path exists as a file.
byte[] ReadAllBytes(string path)
Source
Reads all bytes from a file.
long GetLength(string path)
Source
Returns the current file length in bytes.
string ComputeSha256(string path)
Source
Computes a lowercase SHA-256 digest for a file.
IEnumerable<string> EnumerateFiles(string rootPath)
Source
Enumerates files under an exact release tree.
AppSurfaceDocsReleaseArchiveFileSystem Physical { get; }
Source
Gets the physical filesystem adapter used by runtime verification.
Immutable file metadata from a verified release archive manifest.
PathArchive-root-relative file path using slash separators.LengthExpected byte length.ContentTypeContent type captured by export, when known.Sha256Expected lowercase SHA-256 digest.Verified release archive metadata used by mounted published-tree handlers.
bool TryGetFile(string relativePath, out AppSurfaceDocsReleaseArchiveFile file)
Source
Attempts to resolve verified metadata for a path the published-tree handler is about to serve.
relativePathArchive-root-relative path using either platform or slash separators.fileVerified file metadata when present.true when the path is covered by the release manifest.
int FileCount { get; }
Source
Gets the number of files covered by the verified release manifest.
AppSurfaceDocsFrozenRouteManifest FrozenRouteManifest { get; }
Source
Gets the route manifest parsed from verified release archive bytes.
Stable archive verification diagnostic surfaced through logs and sanitized availability messages.
CodeStable diagnostic code.PublicMessageSanitized public message.DetailOperator-facing detail suitable for structured logs.PathArchive-root-relative path associated with the failure, when applicable.Describes the archive-integrity state resolved for a published AppSurface Docs version.
Numeric values are explicit and stable because catalog diagnostics, command output, and downstream consumers may serialize or persist these states outside the current process.
Describes one non-fatal AppSurface Docs metadata authoring problem discovered while parsing or normalizing metadata.
CodeStable diagnostic code suitable for tests, logs, and documentation.FieldPathMetadata field path associated with the warning, such as featured_page_groups[0].pages.ProblemReader-facing summary of what is wrong.CauseExplanation of why AppSurface Docs cannot safely use the authored value as-is.FixSuggested author action that resolves the warning.Carries normalized metadata together with non-fatal diagnostics from a Markdown metadata parse.
MetadataThe parsed metadata, or null when no usable metadata document was present.DiagnosticsWarnings produced while parsing or normalizing metadata fields.Harvester implementation that scans C# source files for XML documentation comments.
Task<IReadOnlyList<DocNode>> HarvestAsync(string rootPath, CancellationToken cancellationToken = default)
Source
Collects XML documentation from C# source files under the specified root and produces DocNode entries containing titles, relative file paths with anchors, and HTML-formatted content.
rootPathThe root directory to recursively scan for .cs files.cancellationTokenAn optional token to observe for cancellation requests.A collection of DocNode objects; each contains a title, a relative file path including a fragment anchor, and the extracted HTML documentation.
Skips files in excluded directories (for example "node_modules", "bin", "obj", "Tests", and "examples") and hidden dot-prefixed directories unless explicitly allowlisted. Dot-prefixed files are included. File and directory reparse points are skipped so symlinks and junctions cannot point the built-in harvester outside rootPath.
Task<IReadOnlyList<DocNode>> HarvestAsync(DocHarvestContext context, CancellationToken cancellationToken = default)
Source
Collects XML documentation with the repository-scoped path policy captured for the current aggregation pass.
contextThe harvest context containing the repository root and active path policy snapshot.cancellationTokenAn optional token to observe for cancellation requests.A collection of generated C# API documentation nodes.
This overload is used by the aggregator so VCS ignore exclusions are applied consistently across traversal and file inclusion checks. Custom harvesters continue to use the public HarvestAsync(string, CancellationToken) contract.
string GetMethodId(MethodDeclarationSyntax method, string qualifiedTypeName)
Source
Computes the safe ID for a method to be used in HTML content and stub nodes.
methodThe method declaration syntax.qualifiedTypeNameThe qualified name of the containing type.The safe ID string for the method documentation section.
string GetPropertyId(PropertyDeclarationSyntax property, string qualifiedTypeName)
Source
Computes the safe ID for a property to be used in HTML content and stub nodes.
propertyThe property declaration syntax.qualifiedTypeNameThe qualified name of the containing type.The safe ID string for the property documentation section.
string GetHighlightedDisplaySignature(MethodDeclarationSyntax method)
Source
Generates a syntax-highlighted HTML string representing a method signature for display.
methodThe method declaration syntax.An HTML fragment containing the highlighted signature.
string GetHighlightedPropertySignature(PropertyDeclarationSyntax property)
Source
Generates a syntax-highlighted HTML string representing a property signature for display.
propertyThe property declaration syntax.An HTML fragment containing the highlighted signature.
string GetPropertyAccessorSignature(PropertyDeclarationSyntax property)
Source
Computes the accessors (get/set/init) for a property as a string for inclusion in signatures.
propertyThe property declaration syntax.A string like "{ get; set; }" or "{ get; }".
void AppendHighlightedParameter(StringBuilder builder, ParameterSyntax parameter)
Source
Appends a syntax-highlighted parameter declaration to the provided StringBuilder.
builderThe StringBuilder to append to.parameterThe parameter declaration syntax.string GetDisplayTypeName(TypeDeclarationSyntax typeDecl)
Source
Gets the display name for a type declaration, including generic type parameter placeholders (e.g., <T>).
typeDeclThe type declaration syntax.The display name string.
string GetTypeNameForQualifiedId(TypeDeclarationSyntax typeDecl)
Source
Gets the type name for a qualified ID, appending backtick arity for generic types (e.g., MyType`1).
typeDeclThe type declaration syntax.The type name string used in safe IDs.
bool IsCompilerGeneratedCallerParameter(ParameterSyntax parameter)
Source
Determines whether a parameter is a compiler-generated caller information parameter (e.g., [CallerFilePath]).
parameterThe parameter declaration syntax.true if the parameter should be hidden from documentation; otherwise, false.
string? ExtractDoc(SyntaxNode node)
Source
Extracts XML documentation from the leading trivia of a syntax node and converts it into HTML fragments.
nodeThe syntax node whose leading XML documentation comments will be parsed.The HTML string containing structured documentation sections, or null if no documentation is present or parsing fails.
void AppendTextSection(StringBuilder html, string cssClass, XElement? section, string? heading = null)
Source
Appends a simple text section (like summary or remarks) to the HTML builder.
htmlThe StringBuilder to append to.cssClassThe CSS class name for the section container.sectionThe XElement containing the documentation section.headingOptional heading text for the section.void AppendNamedListSection(StringBuilder html, string cssClass, string heading, IEnumerable<XElement> entries, Func<XElement, string?> keySelector)
Source
Appends a list of named entries (like parameters or exceptions) to the HTML builder.
htmlThe StringBuilder to append to.cssClassThe CSS class name for the section container.headingThe heading text for the section.entriesThe collection of XElements to process.keySelectorA function that extracts the name or key for each entry.string RenderBlockContent(XElement element)
Source
Renders the content of an XElement as block-level HTML (wrapping in paragraphs if necessary).
elementThe XElement to render.An HTML fragment string.
string RenderInlineContent(XElement element)
Source
Renders the content of an XElement as inline HTML.
elementThe XElement to render.An HTML fragment string.
string RenderNodes(IEnumerable<XNode> nodes, bool inlineContext)
Source
Renders a collection of XML nodes into HTML strings.
nodesThe nodes to render.inlineContextIndicates whether rendering occurs in an inline context (affects paragraph handling).The combined HTML string.
string RenderNode(XNode node, bool inlineContext)
Source
Renders a single XML node into its corresponding HTML fragment.
nodeThe node to render.inlineContextIndicates whether rendering occurs in an inline context.The HTML fragment string.
string NormalizeWhitespace(string value)
Source
Normalizes whitespace in the provided string by replacing all whitespace sequences with a single space.
valueThe string to normalize.The normalized string.
void AddOutlineItem(NamespaceDocPage namespacePage, string title, string id, int level)
Source
Adds an outline item to a namespace page when the entry is complete and its target ID has not already been recorded.
namespacePageThe namespace page receiving the outline item.titleThe reader-facing outline title.idThe fragment identifier for the rendered documentation section.levelThe normalized outline level.string? SimplifyCref(string? cref)
Source
Simplifies a "cref" attribute value by removing the type prefix (e.g., "M:", "T:").
crefThe cref value to simplify.The simplified string, or null if the input was empty.
bool IsCompilerGeneratedDocParameter(string? parameterName)
Source
Determines whether a parameter name corresponds to a compiler-generated caller information parameter.
parameterNameThe name of the parameter to check.true if it is a compiler-generated parameter; otherwise, false.
NamespaceDocPage GetOrCreateNamespacePage(IDictionary<string, NamespaceDocPage> namespacePages, string namespaceName)
Source
Gets an existing NamespaceDocPage for the specified namespace name, or creates a new one if it doesn't exist.
namespacePagesThe dictionary of existing pages.namespaceNameThe dotted namespace name.The retrieved or newly created page.
void EnsureNamespaceHierarchy(IDictionary<string, NamespaceDocPage> namespacePages)
Source
Builds the hierarchical structure for namespaces, ensuring parent pages exist and child links are added back into the content. Rebuilds namespacePages in place keyed by NamespaceDocPage.Path.
namespacePagesThe dictionary containing all unique namespace pages encountered during harvesting.string GetNamespaceName(SyntaxNode node)
Source
Extracts the dotted namespace name for a given syntax node by traversing its ancestors.
nodeThe syntax node to process.The full dotted namespace name, or "Global" if none is found.
string BuildNamespaceDocPath(string namespaceName)
Source
Constructs the relative documentation route path for a given namespace name.
namespaceNameThe dotted namespace name.The relative route path string (e.g., "Namespaces/MyNamespace").
string GetNamespaceTitle(string fullNamespace)
Source
Derives a display title for a namespace name.
fullNamespaceThe dotted namespace name.The display title; returns the last segment of the namespace or "Namespaces" for the root.
string GetParentNamespace(string namespaceName)
Source
Gets the parent namespace name for a dotted namespace string.
namespaceNameThe dotted namespace name.The parent namespace name, or an empty string if it is a root namespace.
string GetQualifiedName(BaseTypeDeclarationSyntax node)
Source
Builds the dot-delimited qualified name for a type or enum declaration, including enclosing types and namespaces.
nodeThe type or enum declaration syntax node to compute the qualified name for.The qualified name as a dot-delimited string containing nested type and namespace segments.
Represents a single documentation page for a C# namespace, accumulating content from types within it.
Renders encoded harvest progress HTML fragments for the AppSurface Docs observatory.
The renderer accepts already-redacted progress snapshots and emits bounded markup for full pages and Turbo stream updates. It HTML-encodes text and attribute values, truncates activity to eight entries and diagnostics to four, and treats AppSurfaceDocsHarvestRunState.Completed and AppSurfaceDocsHarvestRunState.Failed as terminal states. The renderer intentionally does not accept request-derived return URLs; Razor views should emit those values through normal attribute or link rendering after validating that they are app-relative.
string Render(AppSurfaceDocsHarvestProgressSnapshot snapshot, int completionDelayMilliseconds)
Source
Renders the observatory fragment for a harvest progress snapshot.
snapshotThe redacted snapshot to render.completionDelayMillisecondsThe completion navigation delay in milliseconds.An encoded HTML fragment for the observatory surface.
When the snapshot is completed, the fragment includes the completion marker and delay used by the client script to refresh after the configured delay. Failed runs render diagnostics but do not auto-refresh. The containing Razor view owns request-derived return links so they are emitted by Razor's encoder rather than this raw fragment.
IHtmlContent RenderHtml(AppSurfaceDocsHarvestProgressSnapshot snapshot, int completionDelayMilliseconds)
Source
Renders the observatory fragment as HTML-safe content for Razor views.
snapshotThe redacted snapshot to render.completionDelayMillisecondsThe completion navigation delay in milliseconds.HTML-safe observatory markup with all dynamic text encoded.
Use this method from package-owned Razor views instead of Html.Raw. Request-derived navigation values belong in the surrounding view model and should be emitted by Razor attributes or anchors after validation.
string RenderTurboStream(AppSurfaceDocsHarvestProgressSnapshot snapshot, int completionDelayMilliseconds)
Source
Renders a Turbo stream update for the harvest observatory target.
snapshotThe redacted snapshot to render.completionDelayMillisecondsThe completion navigation delay in milliseconds.A Turbo stream update that replaces the observatory content.
The outer page owns navigation timing and request-derived return links; the emitted fragment only carries completion state for client-side refresh scheduling.
Builds canonical AppSurface Docs URLs for one AppSurface Docs route family.
This builder centralizes the route contract so controllers, view components, views, and client scripts do not each guess how the docs surface is rooted. RouteRootPath is the stable route-family root used for archive and exact-version routes. CurrentDocsRootPath is the live source-backed docs root used for current docs, search, and current search-index routes. Most consumers should copy Routes rather than assembling route strings or calling lower-level builder methods directly.
string BuildHomeUrl()
Source
Builds the current live docs home URL.
The current docs home path.
string BuildSearchUrl()
Source
Builds the current live docs search workspace URL.
The app-relative search workspace URL for the current docs surface.
string BuildSearchIndexUrl()
Source
Builds the current live docs search-index URL.
The app-relative search-index URL for the current docs surface.
string BuildSearchIndexRefreshUrl()
Source
Builds the current live docs search-index refresh URL.
The app-relative operator refresh route for the current docs search index. Callers must send SearchIndexRefreshMethod with a valid MVC anti-forgery token and satisfy the host-configured AppSurface Docs search-index refresh policy.
string BuildHarvestUrl()
Source
Builds the current live docs harvest observatory URL.
The app-relative operator harvest observatory URL for the current docs surface.
string BuildHarvestRebuildUrl()
Source
Builds the current live docs harvest rebuild URL.
The app-relative operator rebuild route for the current docs harvest. Callers must send HarvestRebuildMethod with a valid MVC anti-forgery token and satisfy the host-configured AppSurface Docs operator-write policy.
string BuildHealthUrl()
Source
Builds the current live docs harvest health HTML URL.
The app-relative health page URL for the current docs surface.
string BuildHealthJsonUrl()
Source
Builds the current live docs harvest health JSON URL.
The app-relative machine-readable health URL for the current docs surface.
string BuildRouteInspectorUrl()
Source
Builds the current live docs route inspector HTML URL.
The app-relative route inspector URL for the current docs surface.
string BuildRouteInspectorJsonUrl()
Source
Builds the current live docs route inspector JSON URL.
The app-relative machine-readable route inspector URL for the current docs surface.
string BuildMetricsCollectUrl()
Source
Builds the current live docs metrics collection URL.
The app-relative browser metrics ingestion URL for the current docs surface.
string BuildSearchQualityUrl()
Source
Builds the current live docs search-quality diagnostics URL.
The app-relative search-quality diagnostics URL for the current docs surface.
string BuildSectionUrl(DocPublicSection section)
Source
Builds a current-surface public section URL.
sectionThe section whose route should be built.The canonical section URL rooted at the current docs surface.
string BuildDocUrl(string path)
Source
Builds a current-surface canonical document URL.
pathThe source or canonical documentation path.The canonical document URL rooted at the current docs surface.
string BuildDocUrl(string docsRootPath, string path)
Source
Builds a canonical document URL rooted at an explicit docs surface root.
docsRootPathThe app-relative docs root path.pathThe source or canonical documentation path.The canonical document URL.
string BuildAssetUrl(string assetName)
Source
Builds the current-surface search asset URL.
assetNameThe asset file name, such as search.css.The canonical asset URL rooted at the current docs surface.
string BuildVersionRootUrl(string version)
Source
Builds the exact-version root URL for one published docs release.
versionThe exact published version identifier.The canonical root URL for that version.
string BuildVersionDocUrl(string version, string path)
Source
Builds a canonical document URL rooted at a specific exact version.
versionThe exact published version identifier.pathThe source or canonical documentation path.The canonical document URL rooted at the requested version.
string BuildCanonicalHref(string appRelativeCanonicalUrl)
Source
Builds the browser-facing canonical href for an app-relative canonical route.
appRelativeCanonicalUrlThe app-relative canonical route, such as /docs/start.The app-relative canonical route when no public origin is configured, or an absolute public canonical URL when PublicOrigin is set.
AppSurface Docs keeps route identity app-relative until the final render boundary. This method is that boundary for canonical metadata: it joins the configured origin with the already-selected canonical path without changing current, preview, archive, or exact-version route semantics.
string BuildVersionsUrl()
Source
Builds the public archive URL.
The stable archive URL.
bool IsCurrentDocsPath(string? path)
Source
Determines whether the supplied request path is inside the current live docs surface.
pathThe request path to check.true when the path belongs to the current live docs surface; otherwise false.
string? NormalizePublicOriginOrNull(string? publicOrigin)
Source
Normalizes a configured public origin for absolute canonical metadata.
publicOriginThe configured origin, which may be null or already normalized.The normalized origin, or null when the value is blank.
ArgumentExceptionThrown when the configured value is not an origin-only HTTP(S) URL.bool TryNormalizePublicOrigin(string? publicOrigin, out string? normalizedPublicOrigin)
Source
Attempts to normalize a configured public origin for absolute canonical metadata.
publicOriginThe configured origin, which may be null or already normalized.normalizedPublicOriginThe normalized origin, or null when the value is blank.true when the value is blank or a valid HTTP(S) origin; otherwise false.
bool IsUnderRoot(string? path, string docsRootPath)
Source
Determines whether a request path belongs to the supplied docs root.
pathThe incoming request path to evaluate.docsRootPathThe normalized docs root path configured for the live docs surface.true when path resolves to the docs root itself or one of its child routes; otherwise false.
Blank paths always return false. Root-mounted docs (/) use IsLikelyRootMountedDocsPath(string) so only known docs-like routes are treated as current docs traffic. Non-root mounts use case-insensitive exact and prefix matching against {docsRootPath}/....
string JoinPath(string docsRootPath, string relativePath)
Source
Joins a normalized docs root with a relative docs route segment.
docsRootPathThe normalized app-relative docs root path.relativePathThe relative docs route to append.The combined app-relative route path.
Leading slashes on relativePath are ignored. null, empty, and whitespace-only relative paths return the docs root unchanged. When the docs root is /, the result stays root-mounted instead of producing a doubled slash. Callers are expected to pass already-normalized root paths and docs-relative segments rather than arbitrary URLs.
string NormalizeDocsRootPath(string? docsRootPath, bool versioningEnabled)
Source
Normalizes a configured docs root into the app-relative route contract AppSurface Docs uses at runtime.
docsRootPathThe configured docs root, which may be null, relative-looking, or already normalized.versioningEnabledWhether versioning is enabled and the default should therefore become /docs/next.The normalized app-relative docs root path.
string NormalizeRouteRootPath(string? routeRootPath, string docsRootPath, bool versioningEnabled)
Source
Normalizes a configured route-family root into the app-relative route contract AppSurface Docs uses at runtime.
routeRootPathThe configured route root, which may be null, relative-looking, or already normalized.docsRootPathThe normalized live docs root used when versioning is disabled and no route root is configured.versioningEnabledWhether versioning is enabled and the default route family should remain /docs.The normalized app-relative route-family root path.
bool VersioningEnabled { get; }
Source
Gets a value indicating whether versioning is enabled for the current host.
string CurrentDocsRootPath { get; }
Source
Gets the canonical root path for the current live source-backed docs surface.
string RouteRootPath { get; }
Source
Gets the stable route-family root for this AppSurface Docs instance.
The route root is the parent for the stable entry alias, version archive, and exact-version release trees. It is the same as CurrentDocsRootPath when versioning is disabled, and commonly the parent of the live preview root when versioning is enabled. For example, RouteRootPath=/foo/bar with DocsRootPath=/foo/bar/next keeps the archive at /foo/bar/versions while the live preview stays at /foo/bar/next.
string DocsEntryRootPath { get; }
Source
Gets the docs entry path used as the stable public landing alias.
string DocsVersionPrefixPath { get; }
Source
Gets the stable exact-version prefix for this route family.
string DocsVersionsRootPath { get; }
Source
Gets the stable archive path for this route family.
AppSurfaceDocsRouteReferences Routes { get; }
Source
Gets named AppSurface Docs routes that consumers should prefer over hardcoded route strings.
string? PublicOrigin { get; }
Source
Gets the configured public origin used for absolute canonical metadata, or null when unset.
This value is an origin only, for example https://docs.example.com. It never includes the AppSurface Docs route root because canonical route identity is still owned by BuildDocUrl(string), BuildVersionDocUrl(string, string), and the route manifest.
Named AppSurface Docs routes for one configured route family.
Consumers should prefer this record when they need well-known AppSurface Docs destinations in host code, operator guidance, generated configuration, or documentation. The values are app-relative. Views and other presentation boundaries apply request PathBase separately before sending browser-facing URLs.
void Deconstruct(out string home, out string search, out string searchIndex, out string searchIndexRefresh, out string versions)
Source
Deconstructs the original route set for callers that used the positional record contract.
homeThe current live docs home route.searchThe current live docs search workspace route.searchIndexThe current live docs search-index JSON route.searchIndexRefreshThe authenticated search-index refresh route.versionsThe route-family archive route.void Deconstruct(out string home, out string search, out string searchIndex, out string searchIndexRefresh, out string versions, out string health, out string healthJson)
Source
Deconstructs the route set through harvest health diagnostics.
homeThe current live docs home route.searchThe current live docs search workspace route.searchIndexThe current live docs search-index JSON route.searchIndexRefreshThe authenticated search-index refresh route.versionsThe route-family archive route.healthThe current live docs harvest health HTML route.healthJsonThe current live docs harvest health JSON route.void Deconstruct(out string home, out string search, out string searchIndex, out string searchIndexRefresh, out string versions, out string health, out string healthJson, out string routeInspector, out string routeInspectorJson)
Source
Deconstructs all known routes including diagnostics routes.
homeThe current live docs home route.searchThe current live docs search workspace route.searchIndexThe current live docs search-index JSON route.searchIndexRefreshThe authenticated search-index refresh route.versionsThe route-family archive route.healthThe current live docs harvest health HTML route.healthJsonThe current live docs harvest health JSON route.routeInspectorThe current live docs route inspector HTML route.routeInspectorJsonThe current live docs route inspector JSON route.void Deconstruct(out string home, out string search, out string searchIndex, out string searchIndexRefresh, out string versions, out string harvest, out string harvestRebuild, out string health, out string healthJson, out string routeInspector, out string routeInspectorJson)
Source
Deconstructs all known routes including harvest rebuild and diagnostics routes.
homeThe current live docs home route.searchThe current live docs search workspace route.searchIndexThe current live docs search-index JSON route.searchIndexRefreshThe authenticated search-index refresh route.versionsThe route-family archive route.harvestThe current live docs harvest observatory route.harvestRebuildThe authenticated harvest rebuild route.healthThe current live docs harvest health HTML route.healthJsonThe current live docs harvest health JSON route.routeInspectorThe current live docs route inspector HTML route.routeInspectorJsonThe current live docs route inspector JSON route.string Home { get; init; }
Source
Gets the current live docs home route.
string Search { get; init; }
Source
Gets the current live docs search workspace route.
string SearchIndex { get; init; }
Source
Gets the current live docs search-index JSON route.
string SearchIndexRefresh { get; init; }
Source
Gets the authenticated search-index refresh route.
string SearchIndexRefreshMethod { get; init; }
Source
Gets the HTTP method callers must use with SearchIndexRefresh.
string Harvest { get; init; }
Source
Gets the current live docs harvest observatory route.
string HarvestRebuild { get; init; }
Source
Gets the authenticated harvest rebuild route.
string HarvestRebuildMethod { get; init; }
Source
Gets the HTTP method callers must use with HarvestRebuild.
string Versions { get; init; }
Source
Gets the route-family archive route, whether or not versioning endpoints are currently enabled.
string Health { get; init; }
Source
Gets the current live docs harvest health HTML route.
string HealthJson { get; init; }
Source
Gets the current live docs harvest health JSON route.
string RouteInspector { get; init; }
Source
Gets the current live docs route inspector HTML route.
string RouteInspectorJson { get; init; }
Source
Gets the current live docs route inspector JSON route.
string MetricsCollect { get; init; }
Source
Gets the current live docs metrics collection route.
string SearchQuality { get; init; }
Source
Gets the current live docs search-quality diagnostics route.
Enforces AppSurface Docs' reserved harvest-stream gate before host RazorWire stream authorizers run.
This filter protects the docs-owned harvest progress channel even when a host registers an IRazorWireStreamAuthorizer after AddAppSurfaceDocs() and thereby replaces the normal Docs wrapper in Microsoft DI. The filter only gates the reserved AppSurface Docs channel; non-docs channels continue to the host authorizer unchanged.
ValueTask<AppSurfaceAuthResult?> AuthorizeAsync(RazorWireStreamAuthorizationContext context)
Source
Applies hidden-route and shared read-policy checks to the AppSurface Docs harvest progress channel.
contextCurrent stream authorization context.null for non-docs channels or when the normal Docs wrapper is already active; otherwise a gate result that denies hidden routes or read-policy failures and lets host authorizers narrow successful reads.
Publishes the public route identity graph for one AppSurface Docs snapshot.
The manifest is an internal read model, not a second router. It contains only public canonical winners plus redirect aliases that should recover to those winners. Collision losers, reserved routes, and invalid routes stay represented by diagnostics so export and future consumers do not guess a destination for ambiguous paths.
Describes one public AppSurface Docs page and the aliases that should redirect to it.
SourcePathHarvested repository-relative source path for the canonical winner.CanonicalRoutePathDocs-root-relative canonical route path.CanonicalLiveUrlApp-relative live URL for the canonical route.RecoveryAliasesImplicit source-shaped Markdown aliases for paste recovery.DeclaredAliasesAuthor-declared redirect aliases from metadata.SourcePathIsMarkdownWhether SourcePath is a Markdown document.Describes one route alias that redirects to a canonical AppSurface Docs page.
RoutePathDocs-root-relative alias route path.LiveUrlApp-relative live URL for the alias.KindThe alias source category.Identifies why an AppSurface Docs route alias exists.
Exposes non-fatal diagnostics captured by a harvester during its most recent successful run.
The aggregate health snapshot reads these diagnostics only after the harvester participates in the current snapshot. Failed, timed-out, canceled, or disabled harvesters surface through the primary harvester-health diagnostic instead.
IReadOnlyList<DocHarvestDiagnostic> GetHarvestDiagnostics()
Source
Gets the diagnostics captured during the most recent completed harvester run.
Structured diagnostics suitable for inclusion in the aggregate harvest-health snapshot.
Normalizes harvested non-Markdown source paths into legacy browser-facing routes used by AppSurface Docs.
A harvested source path is the repository-relative or generated path assigned to a DocNode before AppSurface Docs publishes it. This helper is intentionally narrow: it exists for generated or imported non-Markdown sources such as XML API reference pages, generated JSON or YAML API specs, and imported HTML fragments that still use the historical .html route shape. Markdown documents should use DocRouteIdentityCatalog instead, because that catalog owns clean public routes, redirect aliases, collisions, and reserved-route diagnostics.
string BuildCanonicalPath(string sourcePath)
Source
Constructs a legacy browser-facing path for a harvested non-Markdown documentation source path.
sourcePathThe harvested source path, optionally including a fragment.The legacy docs route path, including the .html suffix used by generated API docs and any original fragment identifier. Markdown public routes are owned by DocRouteIdentityCatalog.
Use this method only when a source must keep the generated-docs compatibility contract where Namespaces/Foo.Bar becomes Namespaces/Foo.Bar.html. It trims leading and trailing separators, preserves fragments, normalizes backslashes to slashes, and appends .html unless the final file name already has that suffix. The word "canonical" in the method name refers to this legacy generated-doc route canonicalization; it does not mean the clean Markdown route contract. For authored Markdown pages, callers should query DocRouteIdentityCatalog so explicit canonical_slug, redirect aliases, public-route collisions, and reserved-route checks all remain centralized.
Sanitizes harvested AppSurface Docs HTML with the package-owned allowlist for docs content and highlighted code blocks.
AppSurface Docs starts from the HtmlSanitizer defaults, adds the package-owned content tags section, article, header, details, summary, pre, code, and span, and allows the class, id, open, and package-owned data-doc-code-language attributes. The sanitizer explicitly removes style from the default attribute set so harvested Markdown, generated API HTML, and server-side highlighted code can keep structural hooks while inline presentation and unsafe markup are stripped. Callers should pass already-rendered AppSurface Docs HTML through this sanitizer before display or export; supported semantic wrappers and token spans are preserved, while scripts, event handlers, inline styles, and unsupported attributes are discarded.
string Sanitize(string html)
Source
Sanitizes rendered documentation HTML using the AppSurface Docs allowlist.
htmlThe rendered HTML to sanitize.Safe HTML that preserves AppSurface Docs structural markup and removes unsupported tags or attributes.
Validates physical paths that belong to the AppSurface Docs trusted release store.
The guard is intentionally filesystem-backed and fail-closed. AppSurface Docs uses it before mounting published release trees and before reading request-time files from a mounted tree so catalog metadata cannot redirect a public docs route outside the operator-owned release store.
string NormalizePhysicalPath(string path)
Source
Converts a filesystem path to a full physical path and removes a trailing directory separator.
pathThe path to canonicalize with Path.GetFullPath(string).The canonical physical path without a trailing directory separator.
string ResolveConfiguredRoot(string contentRootPath, string? configuredRootPath, string catalogDirectory)
Source
Resolves the trusted release root from configuration, defaulting to the catalog directory when unset.
contentRootPathThe application content root used to anchor relative configured paths.configuredRootPathThe optional operator-configured trusted release root.catalogDirectoryThe catalog directory used as the default trusted root.The canonical trusted release root path.
string ResolveContentRootRelativePath(string contentRootPath, string configuredPath)
Source
Resolves an operator-configured path, preserving rooted paths and anchoring relative paths under the content root.
contentRootPathThe application content root used for relative configured paths.configuredPathThe configured path after caller-side trimming.The full filesystem path represented by configuredPath.
bool TryResolveCatalogTreePath(string trustedReleaseRootPath, string? configuredExactTreePath, out string? exactTreePath, out string? publicIssue, out string? internalDetail)
Source
Resolves a catalog exactTreePath under the trusted release root after rejecting unsafe metadata.
trustedReleaseRootPathThe canonical trusted release root that must contain the release tree.configuredExactTreePathThe catalog value to resolve. It must be relative, non-empty, and free of hidden or parent-traversal segments.exactTreePathReceives the canonical physical release tree path when resolution succeeds.publicIssueReceives the user-safe availability message when resolution fails.internalDetailReceives diagnostic detail for logs and tests when resolution fails.true when the catalog path is safe to validate under the trusted root; otherwise, false.
bool TryValidateDirectory(string directoryPath, string publicMissingIssue, string publicUnsafeIssue, out string? publicIssue, out string? internalDetail)
Source
Validates that a physical directory exists and is not itself a symlink, junction, or reparse point.
directoryPathThe physical directory path to inspect.publicMissingIssueThe user-safe issue to report when the directory is missing.publicUnsafeIssueThe user-safe issue to report when the directory is unsafe or cannot be inspected.publicIssueReceives the user-safe issue when validation fails.internalDetailReceives diagnostic detail for logs and tests when validation fails.true when the directory exists and is ordinary; otherwise, false.
bool TryValidateFileCandidate(string exactTreeRootPath, string relativeFilePath, out string physicalFilePath, out string? denialReason)
Source
Resolves and validates a file candidate beneath an exact release tree root.
exactTreeRootPathThe canonical release tree root that must contain the candidate file.relativeFilePathThe relative file path requested from the release tree. Rooted paths and parent traversal are rejected.physicalFilePathReceives the canonical physical file path when the relative path can be resolved.denialReasonReceives the internal reason when the candidate is unsafe, missing, or unreadable.true when the file candidate stays under the tree and has no reparse segments; otherwise, false.
bool TryValidateNoReparseSegments(string trustedRootPath, string candidatePath, bool expectLeafFile, out string? denialReason)
Source
Walks a candidate path from its trusted root and rejects any symlink, junction, or reparse point segment.
trustedRootPathThe physical root that must contain candidatePath.candidatePathThe physical path to validate.expectLeafFileWhether the candidate leaf is expected to be a file instead of a directory.denialReasonReceives the internal reason when containment or segment validation fails.true when every segment from root to candidate is ordinary; otherwise, false.
bool IsSameOrDescendant(string trustedRootPath, string candidatePath)
Source
Determines whether a canonical candidate path is equal to or physically beneath a trusted root path.
trustedRootPathThe trusted root path to compare after canonicalization.candidatePathThe candidate path to compare after canonicalization.true when the candidate equals the root or starts below it using platform path comparison semantics.
bool IsPathMetadataException(Exception ex)
Source
Classifies exceptions that can occur while normalizing paths or reading filesystem metadata.
exThe exception thrown by path or metadata access.true when callers should convert the exception into a fail-closed validation denial.
bool TryValidateSegment(string path, bool expectFile, out string? denialReason)
Source
Validates one physical path segment as an ordinary file or directory.
pathThe physical path segment to inspect.expectFileWhether the path is expected to be a file; otherwise a directory is expected.denialReasonReceives the internal reason when the segment is missing, a reparse point, or unreadable.true when the segment exists and is not a link or reparse point; otherwise, false.
bool IsLinkOrReparsePoint(FileSystemInfo info)
Source
Detects whether a filesystem entry is a symlink, junction, or other reparse point.
infoThe filesystem metadata entry to inspect.true when the entry has reparse attributes or a link target.
bool ContainsParentTraversal(string path)
Source
Checks catalog metadata for parent-directory traversal segments.
pathThe catalog path value to inspect.true when any path segment is exactly ...
bool ContainsHiddenSegment(string path)
Source
Checks catalog metadata for hidden release tree segments while allowing a leading current-directory marker.
pathThe catalog path value to inspect.true when any non-current-directory segment starts with ..
StringComparer PhysicalPathComparer { get; }
Source
Gets the comparer used for canonical physical paths on the current platform.
StringComparison PhysicalPathComparison { get; }
Source
Gets the comparison mode used when checking whether one canonical physical path contains another.
Evaluates host-owned ASP.NET Core policies for AppSurface Docs diagnostics read surfaces.
This adapter is intentionally local to the Docs package. It keeps the diagnostics-read policy independent from the internal Auth.AspNetCore evaluator while preserving ASP.NET Core policy authentication, challenge, forbid, and setup failure semantics for the harvest progress stream.
ValueTask<AppSurfaceAuthResult> AuthorizeAsync(HttpContext httpContext, string policyName, CancellationToken cancellationToken = default)
Source
Evaluates the configured diagnostics read policy against the current request.
httpContextCurrent HTTP request context.policyNameNon-blank host-owned ASP.NET Core authorization policy name.cancellationTokenCancellation observed before and during policy lookup.A passive AppSurface auth result representing the policy outcome.
Builds the small static recovery-link set shared by browser docs recovery surfaces.
This builder only uses DocsUrlBuilder and built-in public-section route contracts. It must not inspect harvested docs, search snapshots, or the missing request path. That constraint keeps standalone 404 pages deterministic during cold starts and prevents stale or hostile request paths from shaping recovery navigation. Callers that need search-result or representative-document fallback behavior should keep that logic in the search surface and adapt these links only as static route-safe entries.
IReadOnlyList<DocsRecoveryLink> BuildRecoveryLinks()
Source
Builds the route-safe docs recovery links for the current live docs surface.
An ordered link set containing the primary search action, route-contract-backed Start Here and Packages section links, and the docs home link. The section and home links are marked export-ignorable because they are optional recovery affordances on sparse static exports; search remains crawlable so exported 404.html pages keep a useful primary action when the search page is exported.
Logs a startup warning when non-development diagnostics reads are exposed without the shared read policy.
The warning is advisory and does not fail startup. This preserves compatibility for hosts that intentionally enforce diagnostics access through their application pipeline, reverse proxy, or network boundary, while making risky package exposure visible during deployment.
Task StartAsync(CancellationToken cancellationToken)
Source
Logs the advisory startup warning when non-development diagnostics reads are exposed without a configured Diagnostics.OperatorReadPolicy.
cancellationTokenUnused; the startup warning performs no cancellable I/O.A completed task after the advisory check has run.
Startup is never failed by this check. The method returns without logging in Development, when the shared read policy is configured, or when ResolveExposedDiagnosticsSurfaces finds no exposed diagnostics surfaces. Otherwise, it logs the exposed surface names so operators can either configure the read policy or verify that host, reverse-proxy, or network authorization already protects those reads.
Task StopAsync(CancellationToken cancellationToken)
Source
Stops the advisory startup warning service.
cancellationTokenUnused; there is no shutdown work to cancel.A completed task.
The service does not hold background work or subscriptions after StartAsync returns, so stopping is a no-op and always completes synchronously.
Represents the authored Markdown code-fence input that AppSurface Docs can render as highlighted or plain code.
CodeThe literal code block body, or null when an upstream renderer provides no text.LanguageThe first whitespace-delimited Markdown info-string token, or null when omitted.AppSurfaceDocsCodeBlock stores only the first whitespace-delimited info-string token in Language because the renderer treats that token as the language hint. It does not parse full info-string attributes. Language may be null when the info string is omitted, and Code may be null when an upstream renderer provides no text, so callers must handle both defensively.
Describes AppSurface Docs' normalized view of an authored code-fence language token.
NormalizedLanguageThe stable AppSurface Docs language name.ClassLanguageThe safe suffix used by the conventional language-* class.LabelThe reader-facing language label.TextMateLanguageIdThe TextMateSharp language id, or null for plaintext fallback.IsKnownWhether the authored token is part of AppSurface Docs' recognized language catalog.IsPlainTextWhether the language should render as escaped plaintext.Applies AppSurface Docs harvest path rules for source-backed Markdown, C#, and JavaScript documentation.
Policy order is intentionally fixed: built-in source candidates are checked first, then global includes, source-specific includes, default exclusion groups and their allows, repository VCS ignore rules when a snapshot supplies them, global excludes, and source-specific excludes. Default groups protect build output, hidden directories, test projects, and C# source under examples. Directory pruning uses the same defaults and clear /** subtree excludes, but keeps a default-excluded subtree only when a configured group allow can match that directory or one of its descendants.
AppSurfaceDocsHarvestPathPolicy CreateDefault()
Source
Creates a policy with package defaults and no configured include or exclude globs.
AppSurfaceDocsHarvestPathDecision Evaluate(string relativePath, AppSurfaceDocsHarvestSourceKind sourceKind)
Source
Evaluates a repository-relative path and returns the include/exclude decision with rule trace details.
The input must be a normalized relative candidate. Invalid paths are excluded with AppSurfaceDocsHarvestPathDecisionCode.ExcludedByInvalidPath. Include misses use the Miss codes, while configured exclude and default-group denials use their corresponding ExcludedBy* codes.
bool ShouldIncludeFilePath(string relativePath, AppSurfaceDocsHarvestSourceKind sourceKind)
Source
Returns whether relativePath is included by the path evaluator.
IEnumerable<string> EnumerateCandidateFiles(string rootPath, AppSurfaceDocsHarvestSourceKind sourceKind, string searchPattern, CancellationToken cancellationToken)
Source
Enumerates candidate files under rootPath while pruning policy-excluded subtrees.
The searchPattern is applied to each visited directory. File and directory reparse points are skipped before they can be harvested or traversed, so symlinks and junctions cannot point built-in harvesters outside the selected repository root. Cancellation is observed between directory visits.
bool ShouldPruneDirectory(string relativeDirectory, AppSurfaceDocsHarvestSourceKind sourceKind)
Source
Returns whether a normalized repository-relative directory can be skipped during traversal.
Invalid directories are pruned. Default-excluded directories are pruned unless the matched group has an allow glob that can apply to a file inside the directory or a descendant. Configured subtree excludes prune only when a /** pattern matches the directory.
bool IsKnownDefaultGroupId(string? groupId)
Source
Returns whether groupId is a supported named default exclusion group.
Matching is case-insensitive but name-only; numeric enum values are rejected so configuration stays stable.
string NormalizeDefaultGroupId(string? groupId)
Source
Normalizes a named default exclusion group to canonical casing, or returns the trimmed input when unsupported.
Normalizes and validates browser paths used by AppSurface Docs identity options.
Keep this helper as the single policy point for brand asset and home-link paths. The options post-configurator, validator, and resolved identity service all depend on the same rules so path behavior cannot drift between appsettings, environment variables, startup validation, and Razor rendering.
string? NormalizeTextOrNull(string? value)
Source
Trims a text option value and treats blank text as omitted.
valueRaw configured text value.The trimmed text, or null when the value is null, empty, or whitespace.
string NormalizeDisplayName(string? displayName)
Source
Resolves the visible AppSurface Docs display name.
displayNameConfigured display name.The trimmed display name, or AppSurfaceDocsIdentityOptions.DefaultDisplayName when blank.
string? NormalizeBrowserPathOrNull(string? value)
Source
Normalizes a browser path when it is valid and preserves invalid non-blank text for later validation errors.
valueConfigured app-root or application-relative browser path.A normalized path when valid, the trimmed original value when invalid, or null when blank.
string? NormalizeCssHexColorOrNull(string? value)
Source
Normalizes a CSS hex color and preserves invalid non-blank text for later validation errors.
valueConfigured CSS hex color.A lower-invariant CSS hex color when valid, the trimmed original value when invalid, or null when blank.
bool TryNormalizeCssHexColor(string? value, out string? normalizedColor, out string error)
Source
Validates and normalizes a CSS hex color suitable for the package-owned wordmark style variable.
valueConfigured color value.normalizedColorLower-invariant color when valid or blank; otherwise null.errorValidation error message when invalid; otherwise an empty string.True when the value is blank, a three-digit hex color, or a six-digit hex color; otherwise false.
bool TryNormalizeBrowserPath(string? value, out string? normalizedPath, out string error)
Source
Validates and normalizes an app-root or application-relative browser path.
valueConfigured path value.normalizedPathNormalized path when the value is valid or blank; otherwise null.errorValidation error message when invalid; otherwise an empty string.true when the value is blank, starts with /, or starts with ~/ and does not contain remote URL, data URL, query string, fragment, backslash, or traversal segments; otherwise false.
Builds normalized AppSurface Docs metadata defaults and fallbacks for harvested documentation nodes.
DocMetadata CreateMarkdownMetadata(string path, string resolvedTitle, DocMetadata? explicitMetadata, string? derivedSummary)
Source
Creates normalized metadata for a Markdown documentation node without emitting normalization warnings.
pathThe source path used for default section, page-type, and audience inference.resolvedTitleThe resolved display title for the Markdown node.explicitMetadataOptional authored metadata that should override inferred defaults.derivedSummaryOptional summary text derived from the document body.The merged metadata with inferred defaults, normalized nav-group handling, and fallback breadcrumbs.
DocMetadata CreateMarkdownMetadata(string path, string resolvedTitle, DocMetadata? explicitMetadata, string? derivedSummary, ILogger? logger)
Source
Creates normalized metadata for a Markdown documentation node and optionally logs authored nav-group fallback warnings.
pathThe source path used for default section, page-type, and audience inference.resolvedTitleThe resolved display title for the Markdown node.explicitMetadataOptional authored metadata that should override inferred defaults.derivedSummaryOptional summary text derived from the document body.loggerAn optional logger that receives warnings when authored nav_group values do not resolve to a built-in public section and AppSurface Docs falls back to the derived section assignment.The merged metadata with normalized section labels, fallback breadcrumbs, and derived-field flags.
This shared internal entry point normalizes explicit public-section selection, preserves authored metadata where valid, derives title/summary fallback semantics, and rebuilds default breadcrumbs when authors do not supply them explicitly.
DocMetadata CreateApiReferenceMetadata(string title, string namespaceName)
Source
Creates canonical metadata for an API-reference documentation node.
titleThe display title for the API node.namespaceNameThe owning namespace used for component inference and breadcrumb generation.Metadata configured for API-reference navigation, contributor visibility, and namespace breadcrumbs.
string? DeriveComponentFromPath(string path)
Source
Derives the owning AppSurface component name from a documentation path when possible.
pathThe documentation path whose segments should be inspected.The inferred component name, or null when no component hint can be derived.
string? DeriveComponentFromNamespace(string namespaceName)
Source
Derives the owning AppSurface component name from a namespace.
namespaceNameThe namespace to inspect.The inferred component name, or null when the namespace is blank.
Coordinates shared AppSurface Docs harvest work so startup warmup, first requests, and trusted operator rebuilds use one ordered source-backed loop.
Task<DocHarvestHealthSnapshot> EnsureStarted()
Source
Starts or reuses the shared initial harvest task.
The memoized harvest-health task for the current run. A new task is created when no task exists or when the prior task was canceled or faulted.
Callers share one background harvest through this coordinator. The task itself runs with CancellationToken.None so one impatient request cannot cancel warmup for later requests.
ValueTask<AppSurfaceDocsHarvestRebuildRequestResult> RequestRebuildAsync(CancellationToken cancellationToken)
Source
Requests a trusted operator rebuild of the full source-backed docs harvest.
cancellationTokenA token that cancels the request decision before any rebuild is queued.AppSurfaceDocsHarvestRebuildRequestResult.Started when a fresh rebuild started immediately, AppSurfaceDocsHarvestRebuildRequestResult.Queued when the active run will be followed by one rebuild, or AppSurfaceDocsHarvestRebuildRequestResult.AlreadyQueued when a rebuild was already pending.
The shared harvest itself runs with CancellationToken.None. Canceling the operator request cannot cancel a harvest already visible to other docs requests. When a rebuild is queued behind an active run, the active run's completion visit is suppressed so only the superseding rebuild returns the browser to the verified docs context.
Task<bool> WaitForCompletionAsync(TimeSpan waitBudget, CancellationToken cancellationToken)
Source
Waits for the shared initial harvest to complete within the provided wait budget.
waitBudgetThe maximum time to wait. Non-positive values return false immediately.cancellationTokenA token used to cancel the caller's wait operation.true when the harvest task completes within budget; otherwise false.
The caller's token cancels only the wait, not the shared harvest task. Exceptions from the harvest task are observed when the task completes before the timeout.
bool IsFatalHarvestException(Exception exception)
Source
Determines whether a queued rebuild continuation should avoid catching an exception that represents process-level failure rather than recoverable harvest failure.
exceptionThe exception observed from the active harvest task.true for process-fatal exception types; otherwise false.
AppSurfaceDocsHarvestProgressSnapshot CurrentProgress { get; }
Source
Gets the latest redacted harvest progress snapshot published by the reporter.
int CompletionDelay { get; }
Source
Gets the completion-navigation delay, in milliseconds, used by the progress reporter.
bool HasActiveOrQueuedHarvest { get; }
Source
Gets a value indicating whether a harvest is running or a queued rebuild is waiting for the running harvest.
Result of a trusted operator request to rebuild the live AppSurface Docs harvest.
The default enum value, 0, is not a valid rebuild request result. Callers that bind, deserialize, or display unknown values should treat them as "no request result." While a harvest is active, rebuild requests are coalesced so at most one superseding rebuild is queued behind the active run. The numeric member values are part of the public compatibility contract and must not be reordered or renumbered.
Validates document paths carried by published AppSurface Docs search-index payloads.
Published search indexes are archive metadata, not a general URL surface. Archive payloads must store canonical /docs-rooted document paths so the published-tree rewriter can safely rebase them for aliases, exact versions, custom route roots, and request path bases at serve time. The served-path entry point is a defense-in-depth check for already-rebased browser links; it must not be used to validate immutable archive contents.
PublishedSearchIndexPathValidationResult ValidateArchivePath(string? value, PublishedSearchIndexArchivePathContext context)
Source
Validates a path stored in an exact published release tree's search-index.json.
valueThe candidate documents[*].path value.contextThe immutable archive validation context.A structured validation result whose PublishedSearchIndexPathValidationResult.Reason identifies the first rejected condition, or PublishedSearchIndexPathRejectionReason.None when the archive value is safe to store.
Archive validation is intentionally stricter than served-link validation: stored paths must use canonical /docs/... forms and must not include request path bases, custom route roots, origins, or deployment aliases. The checks run from syntax and URL-shape hazards toward docs-root and version-family hazards. When several rejection reasons apply, callers should log or surface only the returned first reason plus PublishedSearchIndexPathValidationResult.RedactedValue; do not log the original value.
PublishedSearchIndexPathValidationResult ValidateServedPath(string? value, PublishedSearchIndexServedPathContext context)
Source
Validates a browser-visible search result path after published-tree rewriting has applied the active docs root.
valueThe candidate browser-visible path.contextThe served docs surface context.A structured validation result whose PublishedSearchIndexPathValidationResult.NormalizedPath is safe to use as a browser-visible link only when PublishedSearchIndexPathValidationResult.IsValid is true.
Served validation accepts the active docs root and optional archive root after route rewriting has already happened. It still rejects executable schemes, off-root links, traversal, encoded separators, controls, and reserved docs endpoints before the value can enter client-side result rendering.
string ToDiagnosticCode(PublishedSearchIndexPathRejectionReason reason)
Source
Converts a rejection reason to the stable lower-kebab diagnostic code used in logs, telemetry, and tests.
reasonThe structured validation reason returned by the policy.One of none, missing, whitespace, not-root-relative, scheme-url, absolute-url, protocol-relative, backslash, control-character, malformed-percent-encoding, encoded-separator, encoded-traversal, outside-docs-root, reserved-route, wrong-version, or unknown for future enum values.
Use PublishedSearchIndexPathRejectionReason for code branching and this string only for stable diagnostics that may cross process, log, or test boundaries.
Immutable context for validating document paths stored in a published exact-version archive.
VersionThe catalog version whose exact tree is being validated.Runtime context for validating already-rebased browser-visible search result paths.
DocsRootPathThe active browser-visible docs root, including request path base if present.ArchiveRootPathThe active browser-visible archive root, including request path base if present.Structured result for a published search-index document path validation attempt.
IsValidWhether the candidate path is safe for the target context.ReasonThe stable branch-friendly rejection category, or PublishedSearchIndexPathRejectionReason.None when valid. Use PublishedSearchIndexDocumentPathPolicy.ToDiagnosticCode(PublishedSearchIndexPathRejectionReason) when a lower-kebab telemetry or log value is required.NormalizedPathThe validated path portion without query string or fragment when valid. This value is safe to log and to use for docs-root matching because unsafe suffixes and rejected inputs never appear here.RedactedValueA non-sensitive description of the rejected input for diagnostics. This is the only rejected-value field callers should include in operator-visible logs or reader-facing availability messages.PublishedSearchIndexPathValidationResult Valid(string normalizedPath)
Source
Creates a successful validation result for a normalized path-only value.
normalizedPathThe safe-to-log path portion with any query string or fragment removed.A valid result with PublishedSearchIndexPathRejectionReason.None.
PublishedSearchIndexPathValidationResult Invalid(PublishedSearchIndexPathRejectionReason reason, string redactedValue)
Source
Creates a rejected validation result with a stable reason and redacted original value.
reasonThe first rejection reason observed by the policy's ordered checks.redactedValueA length-only or otherwise non-sensitive substitute for the rejected value.An invalid result whose NormalizedPath is null.
Stable rejection categories for published search-index document path validation.
Use enum members for local control flow and PublishedSearchIndexDocumentPathPolicy.ToDiagnosticCode(PublishedSearchIndexPathRejectionReason) for telemetry, log fields, and persisted diagnostics. The policy returns the first matching category in its ordered checks, so a value that is both off-root and reserved reports the earlier root or syntax failure.
Provides file-system traversal helpers for source-backed AppSurface Docs harvesting.
Built-in harvesters treat the configured repository root as the read boundary. Candidate traversal therefore skips file and directory reparse points before a harvester can read content through a symlink, junction, or similar filesystem indirection. The helper preserves the existing lazy depth-first traversal shape and lets callers provide their own repository-relative directory-pruning policy.
IEnumerable<string> EnumerateCandidateFiles(string rootPath, string searchPattern, Func<string, bool> shouldPruneDirectory, CancellationToken cancellationToken)
Source
Lazily enumerates candidate files under a repository root while skipping reparse-point files and directories.
rootPathThe absolute repository root to traverse.searchPatternThe file-system search pattern applied to each visited directory.shouldPruneDirectoryCallback that receives a forward-slash repository-relative directory path and returns whether the subtree should be skipped before its descendants are enumerated.cancellationTokenA token observed before each directory expansion.Absolute file paths that match searchPattern and are not reparse points.
File-system enumeration and attribute exceptions intentionally flow to callers, matching the existing harvester behavior for unreadable trees. This keeps operational failures visible instead of silently shrinking the public documentation surface.
bool IsNonReparsePointFile(string filePath)
Source
Returns whether filePath exists and is not a file-system reparse point.
filePathThe absolute file path to inspect.true when the file exists and can be read as a normal source file candidate; otherwise false.
Use this for explicit one-off source reads such as root LICENSE files and Markdown sidecars that do not flow through candidate traversal. Missing files return false. Attribute lookup exceptions still flow so callers do not accidentally hide filesystem failures.
Resolves authored reader-intent landing curation metadata into browser-facing AppSurface Docs featured-page groups.
The resolver accepts the normalized featured_page_groups metadata on a landing document, matches each authored destination against harvested docs by source or canonical path, skips destinations that are missing, hidden, blank, or duplicated, and returns only groups that still contain at least one visible page. Logging is intentionally warning-level because curation mistakes degrade first-run docs navigation without breaking application startup.
IReadOnlyList<DocLandingFeaturedPageGroupViewModel> ResolveGroups(DocNode? landingDoc, IReadOnlyList<DocNode> docs)
Source
Resolves grouped featured-page metadata from landingDoc against the harvested docs corpus.
landingDocThe root or section landing document that owns the curation metadata, or null when the caller has no landing page to resolve.docsThe harvested docs corpus used for destination lookup.A list of resolved featured-page groups ordered by authored group order, then authored position. The method returns an empty list when landingDoc is null, when the landing doc has no featured_page_groups, or when every authored group is filtered out during resolution. Groups with no visible destinations after validation are omitted. Destinations that resolve to docs without a public DocNode.CanonicalPath are skipped with a warning because collision losers and reserved routes are intentionally not linkable. Duplicate destinations are suppressed across all groups, so a page resolved earlier in authored order will not appear again later in the landing. Browser-facing DocLandingFeaturedPageViewModel.Href values are rooted at the current live docs surface from DocsUrlBuilder, not hardcoded to /docs, and authored canonical input paths are matched against that same configured live docs root before the configured route-family root and legacy stable /docs prefix fallback are considered.
Harvests intentionally public JavaScript API doclets from policy-approved plain .js source files.
The harvester is enabled by default through AppSurfaceDocsJavaScriptHarvestOptions.Enabled and scans repository-relative JavaScript candidates that pass the shared harvest path policy. V1 is deliberately strict: broad discovery requires @public, treats @internal, @private, and @ignore as hard exclusions, skips file and directory reparse points before reads or descent, and turns unsupported public shapes into harvest diagnostics instead of partial docs.
Task<IReadOnlyList<DocNode>> HarvestAsync(string rootPath, CancellationToken cancellationToken = default)
Source
Scans policy-approved JavaScript files under the repository root and returns generated AppSurface Docs API nodes.
rootPathThe repository root used to resolve include and exclude globs.cancellationTokenAn optional token to observe while reading and parsing files.Generated group pages plus fragment-addressable stub nodes for harvested JavaScript API items.
Task<IReadOnlyList<DocNode>> HarvestAsync(DocHarvestContext context, CancellationToken cancellationToken = default)
Source
Scans JavaScript sources with the repository-scoped path policy captured for the current aggregation pass.
contextThe harvest context containing the repository root and active path policy snapshot.cancellationTokenAn optional token to observe while reading and parsing files.Generated JavaScript API group pages and fragment-addressable API nodes.
This overload is used by the aggregator so VCS ignore exclusions are applied consistently across traversal and file inclusion checks. Custom harvesters continue to use the public HarvestAsync(string, CancellationToken) contract.
bool IsValidEventDetailPropertyName(string value)
Source
Validates whether a parsed @property name is a supported event detail field contract.
valueParsed member name from the doclet property tag.true when the member name represents a valid detail.* path; otherwise false.
The validator trims whitespace, strips optional JSDoc property wrappers through StripOptionalPropertyWrapper, requires an ordinal detail. prefix, and validates each remaining segment with IsValidEventDetailPropertySegment. Array contracts use a trailing [] on a segment, such as detail.items[] or detail.items[].id. Common pitfalls are blank names, omitted detail. prefixes, empty segments, unsupported characters, and assuming case-insensitive matching.
JavaScriptHarvestCandidate ClassifyHarvestCandidate(string rootPath, string candidatePath)
Source
Classifies a JavaScript harvest candidate before the harvester reads a source file or descends into a directory.
rootPathThe repository root that bounds built-in JavaScript harvesting.candidatePathThe candidate file or directory path to inspect.The candidate's normalized path metadata and safety state.
JavaScriptHarvestCandidate ClassifyHarvestCandidate(string rootPath, string candidatePath, Func<string, FileAttributes> getAttributes)
Source
Classifies a JavaScript harvest candidate with a caller-supplied attribute reader for deterministic boundary tests.
rootPathThe repository root that bounds built-in JavaScript harvesting.candidatePathThe candidate file or directory path to inspect.getAttributesReads file-system attributes for an already-normalized in-root candidate.The candidate's normalized path metadata and safety state.
Captures the normalized path and boundary decision for a JavaScript harvest file-system candidate.
StatusThe pre-read safety state for the candidate.FullPathThe normalized absolute candidate path.RelativePathThe repository-root-relative candidate path used in glob matching and diagnostics.Describes how the built-in JavaScript harvester may treat a file-system candidate before reading or traversal.
Provides stable helpers for host-owned AppSurface Docs RazorWire stream authorization.
Host applications that implement IRazorWireStreamAuthorizer, or legacy IRazorWireChannelAuthorizer compatibility policies, should use IsHarvestProgressChannel(string?) when applying production authorization rules to the AppSurface Docs live harvest progress stream. Prefer the predicate over raw string comparison so future docs-owned stream naming remains centralized.
bool IsHarvestProgressChannel(string? channel)
Source
Determines whether a RazorWire channel is the AppSurface Docs live harvest progress channel.
channelThe requested RazorWire channel name.true when channel exactly matches HarvestProgressChannel; otherwise false. Null, empty, and differently cased channel names do not match.
Describes the include, exclude, or prune outcome for one normalized harvest path.
IncludedIndicates whether the path remains eligible for harvesting.RelativePathThe normalized repository-relative path that was evaluated.SourceKindThe source type whose harvest policy was applied.CodeThe policy decision code explaining the outcome.TraceThe ordered rule matches that contributed to the decision.MatchedDefaultGroupsThe default exclusion groups that matched the path.Renders configured AppSurface Docs wordmarks for package-owned Razor views.
This helper keeps the docs chrome's encoded wordmark markup in one place so layout and landing views share the same highlight splitting, CSS variable placement, and plain-text fallback. It only consumes resolved identity values; the options post-configuration and validator remain the source of truth for trimming, matching, and color safety.
IHtmlContent Render(AppSurfaceDocsResolvedIdentity identity, string cssClass, string elementName = "span")
Source
Renders the resolved identity display name with the optional configured wordmark highlight.
identityResolved identity for the current docs host.cssClassCSS classes for the outer wordmark element.elementNameThe package-owned element to render. Supported values are span and h1.HTML-safe wordmark markup with all display text encoded.
Registers AppSurface Docs' fenced-code renderer with Markdig.
IAppSurfaceDocsCodeHighlighter CreateDefaultHighlighter(ILogger<TextMateSharpAppSurfaceDocsCodeHighlighter> logger)
Source
Creates the default TextMateSharp-backed AppSurface Docs highlighter.
loggerLogger used to emit diagnostics when grammar loading or highlighting falls back.The default AppSurface Docs code highlighter.
Renders Markdown fenced code blocks through AppSurface Docs' highlighter contract.
string? ExtractLanguage(FencedCodeBlock block)
Source
Extracts the first language token from a fenced code block's info string.
blockThe fenced code block to inspect.The first language token, or null when the fence has no info string.
Normalizes browser-facing metadata hrefs that render as plain anchors.
AppSurfaceDocsMetadataHrefPolicyResult NormalizeTrustMigrationHref(string? href)
Source
Normalizes trust.migration.href and classifies it as absent, safe to render, or rejected.
hrefThe authored migration href from Markdown front matter or sidecar metadata.An explicit policy result that distinguishes missing optional metadata from rejected unsafe metadata.
Result returned by metadata href normalization.
StateExplicit policy state for the authored value.HrefThe trimmed href when the value was allowed or rejected; null when absent.AppSurfaceDocsMetadataHrefPolicyResult Absent()
Source
Creates an absent href result for missing optional metadata.
An absent result with no href.
AppSurfaceDocsMetadataHrefPolicyResult Allowed(string href)
Source
Creates an allowed href result.
hrefThe normalized safe href.An allowed result.
AppSurfaceDocsMetadataHrefPolicyResult Rejected(string href)
Source
Creates a rejected href result.
hrefThe normalized href that failed policy validation.A rejected result.
Rewrites harvested documentation links so authored Markdown can use repository-relative source links while the rendered docs experience still navigates through canonical AppSurface Docs routes.
string RewriteInternalDocLinks(string sourcePath, string html, string docsRootPath, DocLinkTargetManifest targetManifest)
Source
Rewrites internal documentation anchors in rendered HTML so they point at canonical AppSurface Docs routes and carry Turbo navigation attributes that keep browser history aligned with frame navigation.
sourcePathThe harvested source path whose content is being rewritten.htmlThe rendered and sanitized HTML fragment to rewrite.docsRootPathThe app-relative docs root path that should own rewritten links.targetManifestManifest of harvested documentation targets that may be rewritten to docs routes.The rewritten HTML fragment.
string PrefixPathBaseForDocsUrls(string html, string docsRootPath, string? requestPathBase, string routeRootPath = DocsUrlBuilder.DocsEntryPath)
Source
Prefixes request PathBase for rooted docs-local anchor hrefs in rendered HTML content.
htmlThe rendered HTML fragment whose rooted docs-local links should honor the current request path base.docsRootPathThe current live docs root that owns source-backed documentation routes.requestPathBaseThe request path base that should prefix rooted docs-local hrefs when present.routeRootPathThe route-family root that owns stable entry, archive, and exact-version routes.The original fragment when no path base applies or no docs-local rooted links are present; otherwise a fragment whose docs-local anchor hrefs are prefixed so mounted apps stay inside their virtual root.
This helper intentionally scopes itself to docs-local routes rather than all rooted links. Authored content may legitimately include other site-rooted links that should remain outside the docs shell, while docs-local links generated by RewriteInternalDocLinks(string,string,string,DocLinkTargetManifest) must stay within the current mounted app.
Resolves normalized AppSurface Docs identity options into render-ready browser chrome values.
The resolved identity intentionally stores only app-relative or app-root paths. Razor views apply the current request path base at render time with Url.PathBaseAware(...), so singleton identity resolution stays safe for virtual-directory deployments and tests that render the same service provider under multiple path bases.
AppSurfaceDocsResolvedIdentity Identity { get; }
Source
Gets the render-ready identity used by AppSurface Docs layouts.
Render-ready AppSurface Docs identity.
DisplayNameVisible docs product name.HomeHrefApp-root or application-relative brand home link.LogoOptional resolved logo.FaviconsResolved favicon link entries.string? WordmarkHighlightText { get; init; }
Source
Gets the first display-name substring rendered with highlight treatment by the built-in docs chrome.
Null means the wordmark is rendered as plain text. The value is already trimmed and confirmed to appear in DisplayName, so Razor views can split the display name without repeating configuration policy.
string? WordmarkHighlightColor { get; init; }
Source
Gets the CSS hex color used for WordmarkHighlightText.
Null keeps highlighted text in the surrounding wordmark color. Non-null values have already passed the AppSurface Docs CSS hex color allowlist.
Render-ready AppSurface Docs logo.
PathApp-root or application-relative image path.AltTextLogo alt text.Render-ready AppSurface Docs favicon entry.
PathApp-root or application-relative favicon path.TypeFavicon MIME type.(string Markdown, DocMetadata? Metadata) Extract(string markdown)
Source
Extracts inline Markdown front matter and returns the remaining Markdown with parsed metadata.
markdownThe Markdown source that may begin with YAML front matter.A tuple containing the Markdown body and parsed DocMetadata when present and valid.
This compatibility wrapper discards parser diagnostics. Invalid inline YAML returns the original Markdown with null metadata, and non-fatal authoring warnings such as invalid curation YAML or migration metadata are intentionally not surfaced. Call ExtractWithDiagnostics when callers need warnings.
(string Markdown, MarkdownMetadataParseResult Result) ExtractWithDiagnostics(string markdown)
Source
Extracts inline Markdown front matter and returns the remaining Markdown with diagnostics-aware metadata.
markdownThe Markdown source that may begin with YAML front matter.A tuple containing the Markdown body and a MarkdownMetadataParseResult whose MarkdownMetadataParseResult.Metadata contains parsed DocMetadata when present.
This is the authoritative internal entry point for inline metadata parsing. Missing front matter returns the original Markdown and an empty diagnostic list. Invalid inline YAML returns a AppSurfaceDocsMetadataDiagnostic instead of throwing, and deliberately preserves the original Markdown so a malformed header remains visible to the reader. Callers should inspect MarkdownMetadataParseResult.Diagnostics for authoring warnings instead of relying on exceptions for inline metadata failures.
DocMetadata? ParseMetadataYaml(string yaml)
Source
Parses a YAML metadata document into normalized documentation metadata.
yamlThe raw YAML content to deserialize.The normalized metadata model, or null when the YAML document is empty or explicitly null.
YamlExceptionThrown when yaml cannot be parsed as YAML.This compatibility wrapper is shared by inline Markdown front matter and paired sidecar metadata files so both authoring styles normalize through the same schema, defaults, and empty-list handling. It returns only the MarkdownMetadataParseResult.Metadata value from ParseMetadataYamlWithDiagnostics(string) and intentionally discards schema, migration, and authoring diagnostics. Call ParseMetadataYamlWithDiagnostics(string) when callers need those warnings in addition to normalized DocMetadata.
MarkdownMetadataParseResult ParseMetadataYamlWithDiagnostics(string yaml)
Source
Parses a YAML metadata document into a diagnostics-aware metadata result.
yamlThe raw YAML metadata document to deserialize.A MarkdownMetadataParseResult containing optional normalized DocMetadata plus any AppSurfaceDocsMetadataDiagnostic warnings produced while normalizing supported metadata fields.
This is the authoritative internal entry point for metadata documents that are already known to be YAML, including sidecar files. Empty documents and explicit YAML null values return null metadata and no diagnostics. An empty mapping literal such as {} still produces a normalized DocMetadata instance whose fields may all be null. YAML syntax errors still throw YamlException so sidecar callers can report the sidecar file failure through their existing error path; schema and migration warnings are returned through MarkdownMetadataParseResult.Diagnostics.
Enumerates the documentation targets harvested into an AppSurface Docs snapshot so link rewriting can avoid guessing from file extensions alone.
The manifest stores both source paths, such as guides/start.md, and canonical browser paths, such as guides/start.md.html. Query strings and fragments are intentionally ignored because anchors and query parameters decorate a page target rather than defining a separate harvested document.
DocLinkTargetManifest FromNodes(IEnumerable<DocNode> nodes)
Source
Creates a manifest from harvested documentation nodes.
nodesThe harvested documentation nodes that may be linked through AppSurface Docs routes.A manifest containing source and canonical target forms for the supplied nodes.
DocLinkTargetManifest FromPaths(IEnumerable<string?> paths)
Source
Creates a manifest from source or canonical documentation paths.
pathsThe documentation paths to register as known link targets.A manifest containing normalized source and canonical target forms.
bool Contains(string? path)
Source
Determines whether the supplied path resolves to a harvested documentation target.
pathA source or canonical documentation path, optionally rooted, queried, or fragmented.true when the normalized target is in the manifest; otherwise false.
Process-local, bounded aggregate read model for hosted AppSurface Docs search-quality review.
This service accepts only validated, sanitized product-intelligence events. It stores no raw event payloads, raw search text, URLs, identity, cookies, request bodies, or free-form comments. The rolling window exists only to bound process memory while preserving enough recent aggregate signal for maintainer diagnostics.
void Record(AppSurfaceProductEventContract contract, IReadOnlyDictionary<string, string> properties)
Source
Records one sanitized docs product-intelligence event into the bounded aggregate model.
contractThe event contract that validated the event.propertiesSanitized string properties returned by the registry.AppSurfaceDocsSearchQualityResponse GetSnapshot(AppSurfaceDocsOptions options)
Source
Creates a maintainer-facing snapshot of the current aggregate model.
optionsCurrent docs options used to report the resolved metrics mode.A defensive aggregate snapshot.
Loads, validates, and resolves the configured AppSurface Docs version catalog.
The service performs best-effort validation so a broken stored release tree becomes unavailable without preventing healthy versions or the live preview surface from loading. Validation is intentionally release-local: every version is checked independently for a readable tree root, the required landing and search pages, the search index, and the shared search runtime assets that exact-version pages depend on. Exact trees are otherwise treated as immutable, self-contained artifacts: outline-aware exports should include the page-local outline runtime they reference, while historical trees are not crawled or upgraded at host startup. Public AppSurfaceDocsResolvedVersion.AvailabilityIssue values are sanitized for archive UI consumption, while filesystem paths and exception details stay in structured logs only.
AppSurfaceDocsResolvedVersionCatalog GetCatalog()
Source
Returns the resolved version catalog for the current host.
The resolved catalog including availability information for each published version. Returns AppSurfaceDocsResolvedVersionCatalog.Disabled when versioning is off for this host, AppSurfaceDocsResolvedVersionCatalog.EnabledWithoutCatalog when versioning is on but no catalog path was configured, and enabled-but-unavailable semantics when a configured catalog or trusted release root could not be loaded into a usable published-release set.
Represents the resolved version catalog used by the current host.
StatusThe high-level catalog resolution state for the current host. This distinguishes successful resolution from the three sentinel states where versioning is disabled, missing a catalog path, or configured but unavailable.CatalogPathThe catalog path associated with the resolved state. This stays null for the Disabled and EnabledWithoutCatalog sentinels, is typically an absolute filesystem path after successful resolution or file-based unavailability checks, and can remain the normalized configured value when Status is AppSurfaceDocsResolvedVersionCatalogStatus.Unavailable because absolute resolution failed before an unavailable catalog result was created.VersionsThe resolved catalog entries in authored catalog order. Entries stay present even when a published tree is unavailable so archive, diagnostics, and fallback experiences can explain the broken release instead of silently hiding it.RecommendedVersionThe resolved recommended version when one is public and available. This can be null when versioning is disabled, no recommendation was configured, the configured identifier did not resolve, or the matching release was hidden or unavailable after validation.Disabled means the host is running with versioning off, so callers should treat the live docs surface as the only public experience and skip published-release archive UI entirely.
EnabledWithoutCatalog means versioning was turned on but no catalog path was configured, so callers can still expose the live preview surface but should not expect any published releases to resolve.
PublicVersions preserves the ordering from Versions after filtering by AppSurfaceDocsVersionVisibility.Public only. Public-but-unavailable releases remain in that list so the archive can surface their degraded status instead of pretending they do not exist.
AppSurfaceDocsResolvedVersionCatalog Disabled { get; }
Source
Gets the sentinel catalog result for hosts where versioning is disabled entirely.
AppSurfaceDocsResolvedVersionCatalog EnabledWithoutCatalog { get; }
Source
Gets the sentinel catalog result for hosts where versioning is enabled but no catalog path was configured.
IReadOnlyList<AppSurfaceDocsResolvedVersion> PublicVersions { get; }
Source
Gets the public versions that should appear in the archive.
This list preserves the authored order from Versions after filtering only by AppSurfaceDocsVersionVisibility.Public. Versions stay in the list even when AppSurfaceDocsResolvedVersion.IsAvailable is false so archive consumers can show degraded-release messaging instead of silently dropping known public releases.
string? AvailabilityIssue { get; init; }
Source
Gets a sanitized catalog-level availability explanation when catalog or trusted-root configuration failed.
Version-level failures continue to live on AppSurfaceDocsResolvedVersion.AvailabilityIssue. This property is for host-level failures such as a missing or unsafe trusted release root where no exact tree can be mounted safely.
Represents one resolved published docs version and its runtime availability.
VersionThe non-null exact published version identifier from the catalog.LabelThe non-null archive label shown to readers; catalog loading falls back to Version when no label is configured.SummaryOptional summary copy shown in the archive, or null when the catalog entry has no non-blank summary.ExactTreePathThe resolved absolute path to the exported exact-version subtree, or null when catalog path resolution failed before an exact tree could be selected.ExactRootUrlThe non-null canonical public root URL for the exact version.SupportStateThe support-state badge surfaced in the archive.VisibilityThe archive visibility state.AdvisoryStateThe release-level advisory state.IsAvailableWhether the exact-version tree validated successfully and may be mounted or recommended.AvailabilityIssueOptional sanitized public-facing availability explanation when IsAvailable is false. Callers should branch on IsAvailable first, then display this value when present; they should not infer availability by parsing message text. Internal logs retain filesystem paths and exception details, but this message is safe to surface in archive UI and reader-facing diagnostics.ReleaseManifestSha256Optional catalog-pinned release manifest digest. The value is null for unpinned legacy catalog entries and for entries whose configured digest is blank; a non-null value means the catalog requested release archive verification, not that verification necessarily succeeded.ArchiveVerificationStateArchive integrity state resolved for the exact-version tree. The default AppSurfaceDocsReleaseArchiveVerificationState.AvailableUnverifiedLegacy represents an available, shape-valid legacy tree without a catalog-pinned release manifest digest. Treat this state as meaningful only when IsAvailable is true; unavailable versions report AppSurfaceDocsReleaseArchiveVerificationState.Unavailable and carry the public failure reason in AvailabilityIssue.VerifiedReleaseArchiveVerified archive file metadata used by runtime mounts. This value is null unless IsAvailable is true and ArchiveVerificationState is AppSurfaceDocsReleaseArchiveVerificationState.AvailableVerified. When non-null, callers may rely on that invariant instead of rechecking catalog digest details.Describes how the current host resolved its published-version catalog state.
Numeric values are explicit and stable because callers may serialize or persist catalog-resolution state across process boundaries.
Enumerates package-defined default exclusion groups used by harvest path policy evaluation.
Member names are stable internal configuration IDs consumed by DisabledGroups and AllowGlobs. Do not rename existing members without a migration plan; append new members for new groups.
Serves one or more published AppSurface Docs trees from static export artifacts.
Published trees are usually exported from the stable /docs surface and then mounted later under the configured route-family root or {RouteRootPath}/v/{version}. This handler resolves extensionless requests back to the exporter’s .html files and rewrites stable-root HTML or search-index payloads so the mounted tree stays version-local.
Task<bool> TryHandleAsync(HttpContext httpContext)
Source
Attempts to serve the current request from one of the configured published trees.
httpContextThe current HTTP request context.true when a published tree handled the request; otherwise false.
Describes one published exact-version tree that should be mounted into the active host.
When multiple AppSurfaceDocsPublishedTreeMount instances overlap, callers should treat the longest MountRootPath as the winning mount because the request handler resolves mounts from most-specific to least-specific roots before serving content. CanonicalRootPath controls only canonical-link metadata: normal navigation, search payloads, assets, and frozen-manifest redirects continue to use MountRootPath.
string MountRootPath { get; }
Source
Gets the request-path root where the tree should appear.
IFileProvider FileProvider { get; }
Source
Gets the static file provider for the tree contents.
string? ExactTreeRootPath { get; }
Source
Gets the resolved physical root for the exact published tree, when path-guard checks should run.
string CanonicalRootPath { get; }
Source
Gets the app-relative route root canonical metadata should prefer for this mount.
AppSurfaceDocsFrozenRouteManifestCache? FrozenRouteManifest { get; }
Source
Gets the lazy cache for the tree's frozen route manifest, when one should be consulted.
AppSurfaceDocsReleaseArchiveVerificationState ArchiveVerificationState { get; }
Source
Gets the archive-integrity state resolved before this tree was mounted.
AppSurfaceDocsVerifiedReleaseArchive? VerifiedReleaseArchive { get; }
Source
Gets verified archive file metadata, when this mount is backed by a catalog-pinned release manifest.
Rewrites stable-root published-tree content so the same artifact can be served from different mount roots.
Rewrites are mount-aware rather than file-aware. The active AppSurfaceDocsPublishedTreeMount decides which root wins, and then the rewriter adjusts exported stable-root URLs so they point at that mounted surface. The default stable /docs surface only needs HTML rewrites when the host adds a non-empty request PathBase; when the mount root and route root are still /docs and no PathBase applies, the exported HTML is already correct and is returned unchanged unless a distinct canonical root or public origin applies.
string RewriteHtml(string html, string mountRootPath, string previewRootPath = "/docs/next", string routeRootPath = DocsUrlBuilder.DocsEntryPath, string? requestPathBase = null, string? canonicalRootPath = null, string? publicOrigin = null)
Source
Rewrites stable-root HTML so docs-local links, assets, and client config point at the supplied mount root.
htmlThe exported HTML document.mountRootPathThe request-path root where the tree is being served.previewRootPathThe live preview docs root that should stay untouched when encountered.routeRootPathThe route-family root that owns archive and exact-version routes.requestPathBaseThe current host path base that should prefix rewritten app-relative docs URLs.canonicalRootPathThe app-relative route root canonical metadata should prefer for this mount.publicOriginThe runtime public origin used for absolute canonical metadata, or null to preserve exported origins.The rewritten HTML document.
This method rewrites exported stable-root docs links, assets, and the inline window.__appSurfaceDocsConfig payload matched by DocsClientConfigRegex so the document behaves like it was originally emitted for mountRootPath. As part of that rewrite, the legacy docsVersionsUrl client field is removed because version archive navigation is rendered server-side. When mountRootPath and routeRootPath are both the default /docs, rewrites only occur if requestPathBase is non-empty so sub-path-hosted apps still emit /some-base/docs/... links.
string RewriteSearchIndexJson(string json, string mountRootPath, string previewRootPath = "/docs/next", string routeRootPath = DocsUrlBuilder.DocsEntryPath, string? requestPathBase = null)
Source
Rewrites a published search-index payload so mounted document URLs stay inside the active docs surface.
jsonThe exported search-index payload.mountRootPathThe request-path root where the tree is being served.previewRootPathThe live preview docs root that should stay untouched when encountered.routeRootPathThe route-family root that owns archive and exact-version routes.requestPathBaseThe current host path base that should prefix rewritten app-relative docs URLs.The original payload when the mount and route root are the default /docs surface without a non-empty path base, when the payload is not a JSON object with a top-level documents array, or when no eligible documents[*].path values require rewriting; otherwise a payload whose rewritten document paths stay inside the mounted docs root.
Only documents[*].path values are rewritten. Other JSON fields, including titles, metadata, and facet payloads, are preserved exactly as exported. Default stable mounts rooted at /docs are a no-op unless requestPathBase is non-empty, because the exported payload already points at the default surface. Preview-root paths, archive paths such as {RouteRootPath}/versions, and already-versioned exact routes such as {RouteRootPath}/v/1.2.3/guide.html are preserved rather than rebased. When a rewrite does occur, the helper prepends the normalized request path base to eligible app-relative URLs, so /docs/guide.html becomes /some-base/docs/v/1.2.3/guide.html for an exact mount at /docs/v/1.2.3. Callers should not expect other JSON fields to change, and they must supply a non-empty requestPathBase if stable mounts need virtual-directory rebasing.
Loads, validates, and writes the frozen route manifest stored inside exported exact-version docs trees.
Frozen manifests are archive-local read models. They preserve the route identity that existed when an exact release tree was exported, but they do not replace DocRouteIdentityCatalog for live source-backed docs.
Task WriteAsync(string outputPath, AppSurfaceDocsRouteManifest routeManifest, CancellationToken cancellationToken)
Source
Writes the frozen route manifest artifact to an export output directory.
outputPathExport output root that represents one exact-version tree.routeManifestLive route manifest captured from the source-backed docs snapshot.cancellationTokenToken observed while writing the artifact.A task that completes once the manifest has been written.
string Serialize(AppSurfaceDocsRouteManifest routeManifest)
Source
Serializes a frozen route manifest document only after strict validation succeeds.
routeManifestLive route manifest captured from the source-backed docs snapshot.The exact JSON payload used for the frozen route manifest artifact.
ArgumentNullExceptionrouteManifest is null.InvalidOperationExceptionThe route manifest contains unsafe, duplicate, colliding, or ambiguous canonical routes or aliases.This writer-facing API validates with the same strict rules used for exact archive pins. It throws instead of dropping unsafe entries when a canonical route is missing or unsafe, when aliases are unsafe, self-referential, or collide with canonical routes, or when duplicate aliases point at different canonical routes. Use it when creating a new frozen manifest from trusted source-backed route metadata. Use the tolerant loading paths when reading an existing archive where an invalid manifest should degrade to an empty alias map instead of failing the caller.
string BuildManifestPath(string outputPath, string fileName)
Source
Builds the on-disk path for a frozen route manifest filename beneath an exact-version export root.
outputPathExport output root that represents one exact-version tree.fileNameManifest filename to place beneath outputPath.The manifest path under the export output root.
AppSurfaceDocsFrozenRouteManifest Load(IFileProvider fileProvider, ILogger logger, string sourceDescription)
Source
Loads a frozen manifest from a mounted exact-version tree provider.
fileProviderFile provider rooted at the exact-version tree.loggerLogger used for malformed-manifest diagnostics.sourceDescriptionHuman-readable tree identity for logs.The parsed manifest, or Empty when the manifest is missing or unusable.
bool TryLoadVerified(byte[] utf8Bytes, out AppSurfaceDocsFrozenRouteManifest manifest, out string issue)
Source
Parses verified frozen route manifest bytes that have already matched a catalog-pinned release archive manifest.
utf8BytesThe exact UTF-8 bytes covered by the verified release archive manifest.manifestThe immutable route manifest when parsing succeeds.issueA human-readable parse or validation issue when parsing fails.true when the verified bytes describe a supported frozen route manifest.
bool TryResolveAlias(string aliasRoutePath, out string canonicalRoutePath)
Source
Attempts to resolve a docs-root-relative alias route to its frozen canonical route path.
aliasRoutePathDocs-root-relative request path.canonicalRoutePathFrozen canonical route path when an alias matches.true when the alias is known by the frozen manifest; otherwise false.
bool IsSafeRoutePath(string? routePath)
Source
Checks whether a docs-root-relative route path is safe to use as a frozen alias or redirect target.
routePathThe docs-root-relative route path, optionally including a canonical fragment.true when the route stays inside the docs archive namespace; otherwise false.
AppSurfaceDocsFrozenRouteManifest Empty { get; }
Source
Gets the empty manifest used when an archive has no frozen route identity or when parsing failed.
Caches the frozen route manifest for one mounted exact-version tree.
AppSurfaceDocsFrozenRouteManifest GetManifest(ILogger logger)
Source
Returns the cached manifest, loading it from the tree on first use.
loggerLogger used when a present manifest cannot be loaded.The cached frozen route manifest, or an empty manifest when unavailable.
bool UsesVerifiedSnapshot { get; }
Source
Gets a value indicating whether this cache is backed by manifest data parsed during release archive verification.
Captures locale identity, grouping, and diagnostics for one docs snapshot.
EnabledWhether localization graph behavior was enabled when the snapshot was built.DefaultLocaleConfigured default locale, or null when disabled or unavailable.DocSetsTranslation-key groups ordered for deterministic consumption.VariantsBySourcePathCase-insensitive lookup from normalized source path to the first resolved variant.DiagnosticsNon-fatal authoring diagnostics emitted while resolving localization facts.The graph is the Phase 1 localization handoff between harvesters, route identity, navigation, fallback, and search projection code. Consumers should use this graph instead of re-inferring locale state from source paths.
Groups localized variants that represent the same conceptual documentation page.
TranslationKeyStable page identity shared across locale variants.DefaultLocaleSourcePathSource path for the default-locale variant, or null when missing.VariantsResolved variants for this translation key, ordered by locale and source path.FallbackModeEffective missing-translation fallback behavior for the group.Describes one resolved localized document variant.
SourcePathNormalized source path for the variant.LocaleConfigured locale code selected for the variant.TranslationKeyStable translation identity used to group variants.TitleLocalized title, metadata title, or harvested title in precedence order.PublicRoutePathExisting public route path for the base document identity, or null when unavailable.LocaleFallbackOptional page-level fallback override authored by the variant.LocaleWasInferredWhether locale came from inference/defaulting rather than explicit metadata.TranslationKeyWasInferredWhether translation key came from source path inference.Locale and translation key may be authored in metadata, inferred from a configured filename suffix, inferred from a locale folder when a translation key is authored, or defaulted to the configured default locale. Public route paths are null when route identity rejected the source path.
Builds the locale-aware document graph used by later route, navigation, fallback, and search projections.
This Phase 1 builder is intentionally internal. It records document identity and locale facts without changing the existing visible route or search behavior. Later slices should consume this graph instead of re-inferring locale state in controllers, views, or JavaScript.
LocalizedDocsGraph Build(IEnumerable<DocNode> docs, DocRouteIdentityCatalog routeIdentityCatalog)
Source
Builds the localized graph for a docs snapshot.
docsHarvested docs from the snapshot.routeIdentityCatalogRoute catalog for resolving public route candidates.A disabled empty graph when localization is off, otherwise the resolved localization graph.
The builder skips fragment stub source paths, reports unsupported locale signals, duplicate variants, missing bases, folder conflicts, disabled fallback gaps, and conflicting fallback overrides as diagnostics, then returns whatever safe graph data it can derive.
Sanitizes rendered AppSurface Docs HTML using the package's docs-specific allowlist.
IAppSurfaceDocsHtmlSanitizer exists to normalize rendered package documentation fragments before AppSurface Docs displays them. It is not a general-purpose user-generated-content sanitizer, JavaScript policy, or replacement for a host-owned Content Security Policy. Unsupported elements or attributes may be removed.
string Sanitize(string html)
Source
Sanitizes the provided HTML fragment.
htmlThe rendered AppSurface Docs HTML fragment to sanitize.The sanitized HTML fragment.
Sanitize expects a non-null rendered AppSurface Docs HTML fragment, not a complete document or unrendered template. Implementations should throw ArgumentNullException when html is null and should preserve already-encoded text rather than double-encoding it.
Highlights Markdown code fences behind AppSurface Docs' internal HTML contract.
Use this abstraction when Markdown rendering needs package-owned code HTML without coupling callers to a specific highlighter implementation. Implementations must return HTML-safe output for direct insertion into docs rendering templates, including escaped plaintext fallback for unsupported languages or failed highlighting.
AppSurfaceDocsHighlightedCode Highlight(AppSurfaceDocsCodeBlock block)
Source
Renders a code block as either highlighted token markup or escaped plaintext fallback.
blockNon-null code block metadata and source text to render.AppSurface Docs-owned code block HTML. Callers treat the returned value as immutable render output and insert it without additional escaping.
ArgumentNullExceptionThrown when block is null.Applies page-shell heading rules to harvested documentation HTML at render time.
string SuppressLeadingMarkdownH1(string content, bool shellOwnsH1)
Source
Removes the leading rendered Markdown h1 element when the details page shell already renders the page H1.
contentThe sanitized harvested HTML body to render inside the details page content surface.shellOwnsH1true when the page shell renders the semantic H1 from Models.DocDetailsViewModel.Title; false when the harvested body remains responsible for its own top-level heading.The original content when the shell does not own the H1 or when the body does not begin with an h1; otherwise the body with that first rendered h1 removed.
Only the leading H1 is suppressed. Later H1 elements remain visible because they are authored body structure, not duplicated page chrome.
int GetFirstMeaningfulTokenIndex(string content)
Source
Finds the first non-trivia token in harvested HTML.
contentThe harvested HTML body that may begin with whitespace, a BOM, comments, or content.The index of the first non-trivia token, or -1 when the body is empty or contains only ignorable trivia.
Only leading comments are skipped. Other elements, text, or malformed comments remain authored content and are preserved by the suppressor.
Captures one rule evaluation step for internal harvest path diagnostics.
CodeThe decision code represented by the trace step.ScopeThe rule scope, such as global, Markdown, CSharp, or default-allow.PatternThe configured pattern that matched, or null when the step has no pattern match.DefaultGroupThe default exclusion group involved in the step, or null when the step is not group-based.MatchedWhether the rule matched the candidate path.SourcePathThe repository-relative rule source path when a filesystem-backed rule matched.LineNumberThe one-based rule source line number when a filesystem-backed rule matched.Captures the configured harvest path policy and repository VCS ignore policy used for one harvest snapshot.
The snapshot keeps VCS ignore parsing stable for a single aggregation pass and exposes only normalized, repository-relative decisions to harvesters through IHarvestPathPolicy. The configured policy is evaluated first, and VCS ignore decisions are applied as an additional exclusion layer with allow-glob restoration for intentionally public ignored content.
IEnumerable<string> EnumerateCandidateFiles(string rootPath, AppSurfaceDocsHarvestSourceKind sourceKind, string searchPattern, CancellationToken cancellationToken)
Source
Traversal is lazy and depth-first using the shared AppSurface Docs harvest filesystem helper. File paths are yielded as absolute paths, directory paths are normalized to forward-slash repository-relative values before pruning, and directory reparse points are skipped to avoid following symlinks or junctions outside the repository. File reparse points are skipped for the same reason, so symlinked files cannot point harvesters at content outside the repository boundary. Cancellation is checked before each directory expansion, so callers can stop large repository walks without waiting for every descendant to be listed. The method throws ArgumentNullException for a null rootPath or searchPattern; file-system enumeration exceptions are allowed to flow to the harvester so the aggregation layer can report the failure consistently.
AppSurfaceDocsHarvestVcsIgnoreDiagnostics GetVcsIgnoreDiagnostics()
Source
Gets repository VCS ignore loading diagnostics for this harvest snapshot.
VCS ignore status, loaded ignore files, and warning information.
IReadOnlyList<DocHarvestDiagnostic> CreateVcsIgnoreHealthDiagnostics()
Source
Creates health diagnostics that summarize VCS ignore behavior for the snapshot.
Diagnostics safe for harvest health reporting. Sample paths are redacted before client exposure.
Creates per-harvest path policy snapshots for a repository root.
A new snapshot should be created for each aggregation pass so changes to repository ignore files are observed at the beginning of the next harvest while remaining stable for all harvesters in the current pass.
AppSurfaceDocsHarvestPathPolicySnapshot Create(string repositoryRoot)
Source
Creates a path policy snapshot for the supplied repository root.
repositoryRootThe absolute repository root whose VCS ignore files should be read.A snapshot combining configured path rules with repository VCS ignore rules.
Carries repository-scoped harvest dependencies shared by built-in harvesters during one aggregation pass.
RepositoryRootThe absolute repository root that harvesters should scan.PathPolicyThe path policy snapshot exposed through the harvester path-policy contract.The context keeps harvesters on a single policy instance for consistent VCS ignore decisions. Consumers should treat PathPolicy as the authority for traversal and file inclusion, and should not cache the context beyond the aggregation pass that created it.
Contains AppSurface Docs-owned HTML for a rendered Markdown code block.
HtmlThe complete sanitized-shape HTML fragment for the code block.NormalizedLanguageThe normalized language identifier used by AppSurface Docs.IsHighlightedWhether token spans were emitted for the code body.Starts the optional AppSurface Docs harvest warmup and performs the strict harvest-health startup preflight.
The service is always registered by AddAppSurfaceDocs(). By default it starts the same memoized harvest used by docs requests in the background so the first reader does not pay the full cold-start cost. Strict mode reads DocAggregator.GetHarvestHealthAsync(CancellationToken) and fails startup only when the aggregate snapshot is failed.
Task StartAsync(CancellationToken cancellationToken)
Source
Checks harvest health during host startup when strict harvest failure is enabled.
cancellationTokenToken observed while waiting for the cached harvest-health snapshot.A completed task when strict mode is disabled or the aggregate status is not failed.
AppSurfaceDocsHarvestFailedExceptionThrown when AppSurfaceDocsHarvestOptions.FailOnFailure is enabled and the aggregate harvest status is DocHarvestHealthStatus.Failed.Task StopAsync(CancellationToken cancellationToken)
Source
Stops the preflight service.
cancellationTokenUnused cancellation token supplied by the host.A completed task because the preflight owns no background work.
Validates and normalizes repository-relative harvest path patterns and candidate paths.
The validator is stateless and thread-safe. Callers should normalize or validate before matching: backslashes are converted to /, configured patterns are trimmed, and rooted, URI-like, drive-rooted, control-character, query, fragment, and parent-directory paths are rejected. NormalizeSlashes only changes separators; it does not perform validation by itself.
bool IsValidConfiguredGlobPattern(string? pattern)
Source
Returns whether a configured glob pattern is safe to evaluate as a repository-relative path policy rule.
patternThe configured glob pattern. Null, empty, and whitespace-only values are invalid.true when pattern is repository-relative after trimming and slash normalization; otherwise false.
Patterns starting with /, ./, or //, containing ://, drive roots, control characters, ?, #, or a .. segment are invalid. Validation does not prove that a glob can match an existing file; it only rejects unsafe path forms before matcher construction.
bool TryNormalizeCandidatePath(string path, out string normalizedPath)
Source
Attempts to normalize a candidate file or directory path before path-policy evaluation.
pathThe candidate repository-relative path to normalize.normalizedPathReceives the slash-normalized candidate path. When validation fails, this value contains the normalized input that failed validation so callers can log the inspected value.true when path is a non-empty repository-relative path that is safe for policy evaluation; otherwise false.
This method assumes callers have already chosen whether the path represents a file or directory. It trims no whitespace, so caller-provided spaces remain part of the candidate path; unsafe rooted, URI-like, drive-rooted, query, fragment, control-character, and parent-directory forms are rejected.
string NormalizeSlashes(string value)
Source
Converts Windows path separators in value to repository-style slash separators.
valueThe path or pattern value whose separators should be normalized.value with each \ character replaced by /.
This helper does not trim, validate, collapse duplicate separators, or reject unsafe segments. Use one of the validation methods when accepting configuration or candidate paths from outside the policy implementation.
Regex DriveRootedPatternRegex()
Source
Generates the expression used to reject Windows drive-rooted patterns after slash normalization.
A culture-invariant regular expression that matches values such as C:/repo.
Captures redacted live harvest progress and publishes bounded RazorWire updates for late-subscribing docs pages.
ValueTask<string> BeginRunAsync(IReadOnlyList<string> harvesterTypes)
Source
Begins a new harvest run and publishes the initial waiting snapshot.
harvesterTypesThe redacted harvester type names expected in the run.The generated run identifier used to correlate later progress callbacks.
The snapshot update is protected by the reporter gate and PublishAsync is invoked after the lock is released. Passing null throws ArgumentNullException.
string? SuppressCompletionVisitForCurrentOrNextRun()
Source
Suppresses the terminal browser visit for the active run, or for the next run if the coordinator has scheduled work before the run has published its identifier.
The run identifier that was suppressed, or null when suppression was deferred to the next run.
A run can be terminal in the snapshot while its asynchronous completion publish is still in progress. Completed snapshots are therefore still eligible for suppression so a queued rebuild cannot race with a stale terminal visit.
ValueTask RebuildQueuedAsync(string? supersededRunId)
Source
Records that a trusted rebuild is queued behind the active run.
supersededRunIdThe active run identifier returned by SuppressCompletionVisitForCurrentOrNextRun, or null when the suppression applies to the next unpublished run.A task that completes after any snapshot publication attempt.
When a run identifier is supplied, the queued-state update is ignored if a newer run has already replaced the superseded snapshot. This keeps delayed queued notifications from decorating a fresh rebuild run.
ValueTask HarvesterStartedAsync(string runId, string harvesterType)
Source
Marks a harvester as running for the correlated harvest run.
runIdThe run identifier returned from BeginRunAsync.harvesterTypeThe harvester type to update.A task that completes after any snapshot publication attempt.
ValueTask HarvesterCompletedAsync(string runId, string harvesterType, DocHarvesterHealthStatus status, int docCount)
Source
Marks a harvester as terminal and records its document count.
runIdThe run identifier returned from BeginRunAsync.harvesterTypeThe harvester type to update.statusThe terminal health status reported by the harvester.docCountThe non-negative document count reported by the harvester.A task that completes after any snapshot publication attempt.
ValueTask HarvesterDocumentCountUpdatedAsync(string runId, string harvesterType, int docCount)
Source
Updates the in-progress document count for a harvester.
runIdThe run identifier returned from BeginRunAsync.harvesterTypeThe harvester type to update.docCountThe current non-negative document count.A task that completes after any snapshot publication attempt.
ValueTask ActivityAsync(string runId, string message)
Source
Adds a bounded activity message to the current run.
runIdThe run identifier returned from BeginRunAsync.messageThe redacted activity message to prepend.A task that completes after any snapshot publication attempt.
Messages are kept newest-first and capped to the renderer's activity budget. A stale runId is ignored, and blank messages throw ArgumentException.
ValueTask CompleteRunAsync(string runId, DocHarvestHealthSnapshot health)
Source
Completes the correlated run from the final harvest-health snapshot.
runIdThe run identifier returned from BeginRunAsync.healthThe final redacted health snapshot used to populate terminal state, counts, and diagnostics.A task that completes after any snapshot publication attempt.
A failed aggregate health status maps to AppSurfaceDocsHarvestRunState.Failed; all other terminal statuses map to AppSurfaceDocsHarvestRunState.Completed. A stale runId is ignored.
bool IsQueuedRebuildRun(string? supersededRunId)
Source
Determines whether a queued-rebuild status update still belongs to the currently retained running snapshot.
supersededRunIdThe run identifier captured when the rebuild was queued, or null when the queue marker was deferred until the next run published its identifier.true when the current snapshot is the run that should carry queued state.
This guard prevents delayed queued-state publishes from decorating a fresh rebuild after the original run has already completed. Callers must hold _gate while invoking this helper so the snapshot and deferred marker are compared atomically.
AppSurfaceDocsHarvestProgressSnapshot CurrentSnapshot { get; }
Source
Gets the latest redacted harvest progress snapshot.
Access is synchronized through the reporter gate so callers receive a consistent snapshot instance. The returned record is immutable by convention through init-only properties.
int CompletionDelay { get; }
Source
Gets the client-side completion navigation delay in milliseconds.
int SuppressedCompletionVisitCount { get; }
Source
Gets the number of run identifiers whose terminal completion visit is currently suppressed.
This test seam keeps suppression cleanup verifiable without exposing the mutable set or using reflection. Production callers should use SuppressCompletionVisitForCurrentOrNextRun instead of observing this count.
Allows a harvester to publish diagnostics and docs while opting out of aggregate strict-health counts.
Implement this for best-effort harvesters whose diagnostics should be visible to operators but whose empty, failed, timed-out, or canceled state must not decide whether DocHarvestHealthStatus.Failed is returned.
bool ParticipatesInStrictHealth { get; }
Source
Gets a value indicating whether this harvester participates in aggregate strict-health success and failure totals.
Harvester implementation that scans Markdown source files and converts them into documentation nodes.
Task<IReadOnlyList<DocNode>> HarvestAsync(string rootPath, CancellationToken cancellationToken = default)
Source
Harvests Markdown files under the specified root directory and converts each into a DocNode containing a display title, relative path, generated HTML, metadata, and page outline.
rootPathThe root directory to search recursively for `.md` files and an optional root `LICENSE` file.cancellationTokenAn optional token to observe for cancellation requests.A collection of DocNode objects representing each processed Markdown source file, including the display title, path relative to rootPath, generated HTML, metadata, and DocNode.Outline entries when outline headings are available.
Skips files in excluded directories (for example "node_modules", "bin", "obj", and "Tests") and hidden dot-prefixed directories unless explicitly allowlisted. Dot-prefixed files are included. File and directory reparse points are skipped so symlinks and junctions cannot point the built-in harvester outside rootPath. The root LICENSE file is also included when present and not a reparse point so repository-relative license links can resolve in static exports. If a file's name is "README" (case-insensitive), its title is set to the parent directory name or "Home" for a repository root README. The Markdown body is parsed once with Markdown.Parse(markdownBody, _pipeline); HTML is rendered from that AST and DocNode.Outline is populated from the same AST with ExtractOutline, then filtered through the resolved Markdown outline policy so callers can rely on display outline data being present when eligible headings are available. Files that fail to process are skipped and an error is logged.
Task<IReadOnlyList<DocNode>> HarvestAsync(DocHarvestContext context, CancellationToken cancellationToken = default)
Source
Harvests Markdown files with the repository-scoped path policy captured for the current aggregation pass.
contextThe harvest context containing the repository root and active path policy snapshot.cancellationTokenAn optional token to observe for cancellation requests.Markdown documentation nodes generated from eligible repository files.
This overload is used by the aggregator so VCS ignore exclusions are applied consistently across traversal and file inclusion checks. Custom harvesters continue to use the public HarvestAsync(string, CancellationToken) contract.
Task<DocMetadata?> ReadMetadataSidecarAsync(string markdownFilePath, string relativeMarkdownPath, CancellationToken cancellationToken, ICollection<DocHarvestDiagnostic>? harvestDiagnostics = null)
Source
Reads an optional paired sidecar metadata file for a Markdown source document.
markdownFilePathThe absolute Markdown file path.relativeMarkdownPathThe Markdown file path relative to the harvest root.cancellationTokenA token that can cancel sidecar discovery or file reads.harvestDiagnosticsOptional harvest diagnostic collection that receives sidecar metadata warnings.The parsed sidecar metadata, or null when no valid sidecar applies.
AppSurface Docs supports paired metadata files named {file}.yml and {file}.yaml such as README.md.yml. Reparse-point sidecars are ignored so metadata cannot be imported through a symlink or junction outside the harvest root. When both non-reparse extensions exist for the same Markdown file, AppSurface Docs logs a warning and ignores both sidecars until the ambiguity is removed. Inline front matter remains the primary metadata source and overrides any overlapping sidecar fields.
IReadOnlyList<DocOutlineItem> ExtractOutline(MarkdownDocument document)
Source
Extracts page-local outline entries from Markdown heading blocks.
documentThe parsed Markdown document whose heading blocks should be inspected.A source-ordered list of DocOutlineItem values. Each item contains the rendered fragment DocOutlineItem.Id, normalized reader-facing DocOutlineItem.Title, and original heading DocOutlineItem.Level.
Only HeadingBlock descendants with levels between MinOutlineHeadingLevel and MaxOutlineHeadingLevel are included, which means the built-in Markdown harvester emits H2-H3 headings by default. Fragment IDs come from HtmlAttributesExtensions.GetAttributes(heading).Id and titles are produced by NormalizeHeadingText(ExtractInlineText(heading.Inline)). Headings without a non-empty fragment ID or normalized title are silently omitted; consumers and tests should account for those drops and for whitespace normalization when comparing outline titles.
string? ExtractLeadingTitle(MarkdownDocument document)
Source
Extracts the document title from a leading Markdown H1 when one exists.
documentThe parsed Markdown document whose first block may be a page-title H1.The normalized heading text from the leading H1, or null when the document starts with another block or the H1 has no readable text.
This mirrors details-page H1 suppression: only the leading H1 can become package-owned page chrome. Later H1 elements remain body structure and do not replace filename or metadata title fallback behavior.
string ExtractInlineText(ContainerInline? inline)
Source
Extracts plain reader-facing text from a Markdig inline container for outline display.
inlineThe inline container to flatten.The extracted text, or an empty string when no inline content exists.
string NormalizeHeadingText(string value)
Source
Normalizes heading text by collapsing whitespace without introducing leading spaces.
valueThe raw heading text.The normalized heading text.
Identifies the content source type being evaluated by the harvest path policy.
Authorizes the AppSurface Docs harvest progress stream with result-bearing RazorWire authorization semantics.
Use this wrapper when AppSurface Docs owns the harvest-progress channel but must preserve host RazorWire authorization for other channels. The harvest decision order is: harvest route visibility, configured shared diagnostics read policy, optional custom host authorizer narrowing, Development fallback, compatibility custom authorizers, then deny. Built-in RazorWire allow-all and deny-all authorizers are compatibility defaults and do not authorize the Docs harvest stream in non-Development hosts.
ValueTask<AppSurfaceAuthResult> AuthorizeAsync(RazorWireStreamAuthorizationContext context)
Source
Authorizes the requested Docs or host stream channel.
contextThe RazorWire stream authorization context.A passive AppSurface auth result for the stream subscription.
Resolves normalized AppSurface Docs theme options into render-ready CSS variables and shell attributes.
The resolved theme is safe to cache as a singleton because it contains only preset names, density/chrome flags, and sanitized CSS custom property declarations. Razor views emit these values into the exported HTML so live docs, static export, and published archives share the same frozen visual contract.
AppSurfaceDocsResolvedTheme Theme { get; }
Source
Gets the render-ready theme used by AppSurface Docs layouts.
Render-ready AppSurface Docs theme values.
PresetThe selected public preset.DensityThe selected public density.ChromeThe selected public chrome compactness.PresetAttributeKebab-case value emitted in data-docs-theme-preset.DensityAttributeKebab-case value emitted in data-docs-density.ChromeAttributeKebab-case value emitted in data-docs-chrome.RootCssClassCSS classes emitted on the document root.CssVariablesResolved CSS custom properties consumed by the package stylesheets.CssVariableStyleSerialized CSS custom property declarations suitable for a style attribute.Centralizes normalization, validation, and render-ready resolution for the AppSurface Docs theme contract.
Hosts configure AppSurfaceDocsThemeOptions, while this policy keeps every consumer on one resolved theme boundary. Run Normalize during post-configuration before calling Validate or Resolve.
void Normalize(AppSurfaceDocsThemeOptions theme)
Source
Normalizes mutable theme options in place.
themeThe configured theme options to normalize.This method creates omitted nested sections and canonicalizes configured CSS hex colors. Call it during post-configuration before validation or resolution so those later operations observe a stable options shape.
void Validate(AppSurfaceDocsThemeOptions? theme, List<string> failures)
Source
Adds configuration failures for an AppSurface Docs theme.
themeThe normalized theme options to validate.failuresThe destination for actionable validation messages.Contrast checks use the selected preset's canvas and raised backgrounds because v1 intentionally does not expose raw surface overrides. This makes the reported contrast guarantee match the surfaces the package renders.
AppSurfaceDocsResolvedTheme Resolve(AppSurfaceDocsThemeOptions? options)
Source
Resolves theme options into the attributes and CSS variables consumed by rendered and exported docs.
optionsThe normalized theme options to resolve.The immutable theme contract for layouts, search, and static output.
Call Normalize before resolution for configured options. The null-tolerant fallback exists only to keep rendering defensive when no theme section is supplied.
Applies Markdown display-outline rules after metadata defaults and author overrides have been resolved.
IReadOnlyList<DocOutlineItem> Apply(IReadOnlyList<DocOutlineItem> outline, DocMetadata? metadata)
Source
Applies explicit outline metadata and automatic repeated-heading suppression to a harvested Markdown outline.
outlineThe source-ordered harvested Markdown outline entries.metadataThe resolved document metadata, including derived defaults.The outline entries to expose to readers and search heading metadata.
This method filters the display outline only. The rendered Markdown HTML remains unchanged, so fragment links to hidden outline headings can still resolve when readers or search results point directly at the heading.
Cached search-index payload for the live source-backed docs surface.
MetadataStatic metadata emitted alongside the indexed documents.DocumentsSearchable docs entries in the shape consumed by the built-in MiniSearch client.Metadata emitted with each docs search-index payload.
GeneratedAtUtcUTC timestamp for when the snapshot was generated.VersionSchema version understood by the search client.EngineClient-side search engine identifier.Search document entry emitted for the built-in docs search experience.
IdStable identifier for the indexed document.PathBrowser-facing docs URL used for result navigation.TitleDisplay title shown in search results.SummarySummary text favored for recovery and preview UI.HeadingsNormalized heading titles harvested from the document outline.BodyTextFull normalized body text indexed for recall.SnippetShort excerpt shown in search results.PageTypePage-type facet value.PageTypeLabelResolved page-type badge label.PageTypeVariantResolved page-type badge variant.AudienceAudience facet value when explicitly authored.ComponentComponent facet value when explicitly authored.AliasesAlternative phrases that should match the page.KeywordsAdditional authored search keywords.StatusStatus facet value.NavGroupPublic navigation group label when present.PublicSectionResolved public-section slug when the page participates in a public section.PublicSectionLabelHuman-readable public-section label.IsSectionLandingWhether this record is the resolved landing page for its public section.OrderAuthored order hint used for browse sorting.SequenceKeyOptional authored sequence key for related content.CanonicalSlugOptional canonical slug used for route continuity.RelatedPagesAuthored related-page references used for recovery links.BreadcrumbsAuthored breadcrumb labels displayed in result chrome.SourcePathRepository-relative source path retained for provenance and custom integrations.EntryPointsNamespace README entry-point terms projected for richer search consumers.LanguageNormalized programming language for generated API documentation.LanguageLabelReader-facing programming language label for generated API documentation.The Path value is cached relative to the live docs surface root and can be rebased onto a request PathBase at response time without rebuilding the full snapshot. Lists are serialized as JSON arrays so the browser client can preserve exact ordering for headings, aliases, related pages, and breadcrumbs.
Search projection for one namespace README entry point.
LabelReader-facing entry label.SummaryOptional entry summary.TargetResolved generated anchor target when authored.HrefResolved fragment or app-relative href when available.KeywordsAdditional search terms authored on the entry point.Describes a requested search-index projection for the cached docs snapshot.
LocaleOptional active locale code. Blank values are treated as the default projection; non-blank values are trimmed and lower-cased before cache lookup so request casing does not create duplicate entries.IncludeAllLocalesReserved switch for a future all-locales search payload. Phase 1 preserves the existing default payload contract.Caches search-index payloads projected from one immutable docs snapshot.
Phase 1 always returns the default payload, but the cache reserves normalized per-locale keys so later slices can compute locale-specific payloads without changing DocAggregator callers. Access to the mutable projection dictionary is guarded by a private lock, and cache growth is capped because projection values can come from requests.
DocsSearchIndexPayload GetPayload(DocsSearchIndexProjection projection)
Source
Gets the cached payload for a normalized projection.
projectionProjection request to normalize and resolve.The default payload in Phase 1, or a cached projection payload in later localization slices.
When localization is disabled, or when the projection has no locale, the default payload is returned without growing the projection cache. During Phase 1, locale projections also return the default payload; the cache entry is a bounded placeholder for later locale-specific payload generation.
DocsSearchIndexProjection NormalizeProjection(DocsSearchIndexProjection projection)
Source
Normalizes a projection key before cache lookup.
projectionProjection request supplied by the caller.A projection whose locale is null for blank input, or trimmed and lower-case invariant otherwise.
int CachedProjectionCount { get; }
Source
Gets the number of seeded projection entries, exposed so tests can verify the cache bound without reflection.
Service responsible for aggregating documentation from multiple harvesters and caching the results.
Task<IReadOnlyList<DocNode>> GetDocsAsync(CancellationToken cancellationToken = default)
Source
Retrieves all harvested documentation nodes sorted by their Path.
cancellationTokenAn optional token to observe for cancellation requests.A read-only list of all DocNode objects ordered by their Path.
Task<DocHarvestHealthSnapshot> GetHarvestHealthAsync(CancellationToken cancellationToken = default)
Source
Returns structured health for the current AppSurface Docs harvest snapshot.
cancellationTokenAn optional token to observe while waiting for the cached snapshot.Structured harvest health that distinguishes valid empty docs from failed or degraded harvests.
The health snapshot is produced by the same memoized harvest used by GetDocsAsync(CancellationToken). If no docs snapshot exists yet, calling this method triggers the same snapshot generation as a docs read. Caller cancellation cancels only the caller's wait; it does not cancel or poison the shared snapshot computation.
Task<DocNode?> GetDocByPathAsync(string path, CancellationToken cancellationToken = default)
Source
Retrieves a documentation node for a source path or canonical docs path.
pathThe source or canonical documentation path to look up.cancellationTokenAn optional token to observe while waiting for the cached snapshot.The matching DocNode, or null if no node exists for the given path.
The lookup awaits the cached docs snapshot, then delegates to the snapshot's DocPathResolver so legacy source paths, generated canonical .html paths, fragments, separators, and casing follow the same matching rules used by details pages and curated links.
Task<DocRouteResolution> ResolvePublicRouteAsync(string path, CancellationToken cancellationToken = default)
Source
Resolves a requested browser-facing docs route against the current snapshot route catalog.
pathThe non-null request path to resolve. Callers may pass a docs-relative route such as packages, a rooted docs path such as /docs/packages, or a source-shaped path. Markdown source-shaped paths for public winners resolve as redirects to their clean public routes; non-Markdown source paths, collision losers, and reserved routes remain non-public.cancellationTokenAn optional token observed while waiting for the cached docs snapshot.A DocRouteResolution whose kind tells callers whether the request is the canonical public route, a declared or Markdown source-shaped alias that should redirect to DocRouteResolution.PublicRoutePath, an internal non-Markdown source match, a collision or reserved-route loser, or an unresolved path.
This method does not redirect or mutate the snapshot. It awaits GetCachedDocsSnapshotAsync, then delegates to DocRouteIdentityCatalog.ResolvePublicRoute(string) so controllers and link builders branch on the same route identity semantics. Markdown source-shaped redirects let links copied from GitHub or editor paths recover to their published canonical routes instead of falling into the generic 404 page.
Task<AppSurfaceDocsRouteManifest> GetRouteManifestAsync(CancellationToken cancellationToken = default)
Source
Gets the route manifest for the current cached docs snapshot.
cancellationTokenToken observed while waiting for the snapshot.The final public route manifest derived from the same catalog used for live docs routing.
The manifest is produced from the final route identity catalog after namespace README merging and duplicate path resolution. Export consumes this in-process so it can write source-shaped redirect artifacts without crawling an HTTP manifest endpoint or duplicating route rules.
Task<DocDetailsViewModel?> GetDocDetailsAsync(string path, CancellationToken cancellationToken = default)
Source
Builds the typed details view model for the specified documentation page.
pathThe documentation path to resolve.cancellationTokenAn optional token to observe for cancellation requests.A DocDetailsViewModel containing the resolved page, its in-page outline, and wayfinding links, or null when the page cannot be resolved.
Task<DocsSearchIndexPayload> GetSearchIndexPayloadAsync(CancellationToken cancellationToken = default)
Source
Returns the docs search-index payload generated during docs aggregation.
cancellationTokenAn optional token to observe for cancellation requests.A typed payload containing the search metadata and documents emitted by the live docs surface. The payload is cached before response serialization so callers can rebase rooted paths, such as /docs/guide.html, onto a request PathBase without reparsing or reserializing an intermediate JSON node graph.
Task<DocsSearchIndexPayload> GetSearchIndexPayloadAsync(DocsSearchIndexProjection projection, CancellationToken cancellationToken = default)
Source
Returns a cached search-index projection for future locale-aware search consumers.
projectionThe requested search projection key.cancellationTokenAn optional token to observe for cancellation requests.The matching search payload. Phase 1 preserves the existing payload schema for every projection while reserving the snapshot-owned cache seam that localized search will fill in Phase 3.
Task<IReadOnlyList<DocSectionSnapshot>> GetPublicSectionsAsync(CancellationToken cancellationToken = default)
Source
Returns the normalized public-section snapshots derived from the harvested docs corpus.
cancellationTokenAn optional token to observe for cancellation requests.The ordered public sections visible in the current docs snapshot.
Task<DocSectionSnapshot?> GetPublicSectionAsync(DocPublicSection section, CancellationToken cancellationToken = default)
Source
Returns one normalized public-section snapshot when the section is present in the current docs snapshot.
sectionThe public section to resolve.cancellationTokenAn optional token to observe for cancellation requests.The matching section snapshot, or null when the section has no visible public pages.
void InvalidateCache()
Source
Invalidates the cached docs snapshot so docs and search-index are rebuilt on next access.
Task<CachedDocsSnapshot> GetCachedDocsSnapshotAsync()
Source
Retrieves the cached docs snapshot, harvesting docs and generating the search-index payload when absent.
A cached snapshot containing both docs and search-index payload.
When harvesting, each active harvester is invoked; failures from individual harvesters are caught and logged. Contents are sanitized before being cached. If multiple nodes share the same Path, a warning is logged and the first occurrence is retained. The search-index payload is generated from the same harvested snapshot. Caller cancellation does not cancel shared snapshot computation; callers can cancel their own wait. Harvester execution is bounded by a timeout so a single slow harvester cannot block snapshot regeneration indefinitely. The memoized cache entry is created with the configured absolute expiration from AppSurfaceDocsOptions.CacheExpirationMinutes.
Task<DateTimeOffset?> ResolveGitLastUpdatedUtcAsync(string repositoryRoot, string sourcePath, ILogger logger, CancellationToken cancellationToken, Func<string, IReadOnlyList<string>, string, ILogger, CancellationToken, Task<CommandResult>>? executeProcessAsync = null)
Source
Resolves the last committed UTC timestamp for a source path from local git history.
repositoryRootThe repository root used as the git working directory.sourcePathThe repository-relative source path to inspect.loggerLogger used for diagnostic output when git is unavailable or returns unusable data.cancellationTokenCancellation used to abort the lookup when snapshot generation times out.executeProcessAsyncOptional process-execution seam used by tests to simulate git output and failure modes without mutating machine-level PATH state.The exact last-updated UTC timestamp when git returns a parseable ISO 8601 commit date; otherwise null.
IReadOnlyList<DocSectionSnapshot> BuildPublicSections(IEnumerable<DocNode> docs, ILogger logger)
Source
Builds the public-section snapshots from the harvested docs corpus.
docsThe harvested docs to classify.loggerLogger used for section-landing conflict warnings.The ordered public sections that have at least one visible page.
(DocsSearchIndexPayload Payload, int RecordCount) BuildSearchIndexPayload(IEnumerable<DocNode> docs, IReadOnlyList<DocSectionSnapshot> publicSections, DocRouteIdentityCatalog routeIdentityCatalog)
Source
Builds the search-index payload from the harvested documentation nodes.
docsThe documentation nodes to index.publicSectionsThe resolved public sections used to derive landing winners.routeIdentityCatalogThe snapshot route catalog used to emit public canonical paths.A tuple containing the serializable payload and the number of records indexed.
string NormalizeSearchText(string? text)
Source
Decodes HTML entities and normalizes whitespace in the provided text for search indexing.
textThe text to normalize.The normalized text.
string BuildSearchDocUrl(string path)
Source
Constructs a browser-facing URL for a documentation path.
pathThe relative documentation path.A URL string starting with "/docs".
string BuildSearchDocUrl(string docsRootPath, string path)
Source
Constructs a browser-facing URL for a documentation path rooted at a specific docs surface.
docsRootPathThe app-relative docs root path.pathThe relative documentation path.A URL string rooted at docsRootPath.
string TruncateSnippetAtWordBoundary(string text, int maxLength)
Source
Truncates a text snippet at the last word boundary before the maximum length is exceeded.
textThe text to truncate.maxLengthThe maximum allowed length of the snippet.The truncated text with an ellipsis if it was shortened.
IReadOnlyList<DocHarvestDiagnostic> MergeNamespaceReadmes(List<DocNode> nodes, string repositoryRoot, bool renderEntryPointPanel, ILogger logger)
Source
Merges authored namespace-intro content into the corresponding namespace overview pages.
nodesThe list of documentation nodes to process. Consumed namespace README and NAMESPACE.md sources are removed from this list, and unresolved NAMESPACE.md sources are hidden after diagnostics are recorded.repositoryRootRepository root used to resolve colocated project files for NAMESPACE.md intros.renderEntryPointPanelWhether to render validated namespace entry-point metadata into the merged namespace content.loggerLogger used for namespace entry-point target diagnostics.Non-fatal harvest diagnostics produced while merging authored namespace-intro metadata.
string MergeNamespaceIntroIntoContent(string namespaceContent, string readmeContent)
Source
Inserts README content into a namespace overview page after the auto-generated namespace groups.
namespaceContentThe auto-generated HTML content for the namespace page.readmeContentThe HTML content from the README file. A leading rendered Markdown H1 is removed before the README is wrapped because namespace overview pages render their primary H1 in the surrounding details shell.The merged HTML content, with any leading README H1 omitted from the namespace intro section.
int FindMatchingSectionEnd(string content, int sectionStart)
Source
Finds the index of the closing </section> tag that matches a <section> tag starting at the specified index.
contentThe HTML content to search.sectionStartThe starting index of the <section> tag.The index of the closing tag, or -1 if no match is found.
bool IsReadmePath(string path)
Source
Determines whether the specified path points to a documentation README file.
pathThe path to check.true if the path identifies a README.md file; otherwise, false.
string ExtractNamespaceNameFromNamespacePath(string path)
Source
Extracts the dotted namespace name from a documentation path under the "Namespaces/" directory.
pathThe path to process.The extracted namespace name.
string? ExtractNamespaceNameFromReadmePath(string path)
Source
Attempts to extract a namespace name from a README path by looking at the parent directory name.
pathThe README path to process.The extracted namespace name, or null if it cannot be determined.
string? ExtractNamespaceNameFromReadmePath(string path, IEnumerable<string>? knownNamespaceNames)
Source
Extracts a namespace name from a README path, optionally matching against a list of known namespaces.
pathThe README path to process.knownNamespaceNamesOptional list of known namespaces to match directory segments against. When provided, README paths are only treated as namespace introductions when the matching namespace folder appears under a trusted container directory such as docs or Namespaces.The extracted namespace name, or null if it cannot be determined.
bool HasNamespaceReadmePrefix(IReadOnlyList<string> parts, int namespaceStartIndex)
Source
Determines whether the matched namespace folder appears in one of the supported namespace README locations.
partsThe normalized directory path segments that precede README.md.namespaceStartIndexThe index where the matched namespace name begins within parts.true when the namespace folder lives under a trusted container like docs or Namespaces; otherwise, false.
string NormalizeLookupPath(string path)
Source
Normalizes a documentation path for lookup by trimming slashes and removing fragment anchors.
pathThe path to normalize.The normalized lookup path.
string? GetFragment(string path)
Source
Extracts the fragment anchor (after the '#') from a documentation path.
pathThe path to process.The fragment string, or null if no fragment is present.
TimeSpan SnapshotCacheDuration { get; }
Source
Gets the configured absolute lifetime for the shared docs snapshot cache.
Resolves environment-aware visibility for AppSurface Docs harvest health routes and sidebar chrome.
bool AreRoutesExposed(AppSurfaceDocsOptions options, IHostEnvironment environment)
Source
Resolves whether the harvest health controller routes should be registered or allowed for the current host.
This internal helper uses AppSurfaceDocsOptions.Harvest health route settings and the supplied host environment. Missing health options fall back to AppSurfaceDocsHarvestHealthExposure.DevelopmentOnly. Callers must pass the real host environment; the method null-checks both arguments before applying the environment-gated visibility contract.
bool ShouldShowChrome(AppSurfaceDocsOptions options, IHostEnvironment environment)
Source
Resolves whether the sidebar should show harvest health chrome for the current host.
This internal helper uses AppSurfaceDocsOptions.Harvest health chrome settings and the supplied host environment. Missing health options fall back to AppSurfaceDocsHarvestHealthExposure.DevelopmentOnly. Callers use this for presentation chrome only; route exposure is resolved separately by AreRoutesExposed(AppSurfaceDocsOptions, IHostEnvironment). The method null-checks both arguments before applying the environment-gated visibility contract.