Create an API Reference component - #1588
Create an API Reference component#1588Alistair Matthews (alistairmatthews) wants to merge 6 commits into
Conversation
…d on the user's preferred language.
|
Feel free to continue to work on this, but do not merge until I review please. |
David Pine (IEvangelist)
left a comment
There was a problem hiding this comment.
Review summary
Source verification: microsoft/aspire main at 4a8b541c973baeb271f244603b0289c3f47933cf; generated API inputs in this PR point to microsoft/aspire@69db530a4816698cf1d5fa4557933e0ac4f127c6.
Claims: 29 normalized claims - 24 verified, 1 verified with nuance, 0 unverifiable, 4 contradicted.
Doc test: 8 changed routes exercised from PR head e24dac84f3c54cadeb88c0224b381da578d32e2f - 3 critical issues and 1 warning.
This needs another pass before merge. The component currently rewrites a string rather than resolving an API identity, emits no links, breaks inline HTML in real pages, and falls out of sync with supported aspire-lang interactions.
Blocking acceptance criteria
- Accept a canonical fully qualified API identifier and resolve its C# and TypeScript labels plus canonical reference routes from the generated API metadata, including explicit
AspireExportidentities and renames. - Render valid inline link markup. For a missing, ambiguous, or one-language-only symbol, render unlinked code with a precise warning rather than inventing an API.
- Add a separate authoring validator that fails tests/CI for unresolved or ambiguous usages and reports the file, line, supplied FQN, and useful candidates.
- Consume the existing canonical AppHost language state instead of maintaining a second attribute/listener implementation.
- Cover resolver edge cases, parsed browser markup, pointer and keyboard tabs,
PivotSelector, persisted/query initialization, link destinations, and validator diagnostics.
Editor autocomplete or a generated FQN union would be valuable, but it can remain a non-blocking follow-up once deterministic validation exists.
Phase A - Claim verification
Full claim catalog and source evidence
# Phase A - Claims verificationPR: microsoft/aspire.dev #1588
PR head: e24dac84f3c54cadeb88c0224b381da578d32e2f
Source of truth: microsoft/aspire, main, 4a8b541c973baeb271f244603b0289c3f47933cf
Generated reference input represented by the PR: microsoft/aspire@69db530a4816698cf1d5fa4557933e0ac4f127c6
Counts
| Verdict | Count |
|---|---|
| Verified | 24 |
| Verified with nuance | 1 |
| Unverifiable | 0 |
| Contradicted | 4 |
| Total | 29 |
Catalog
| ID | Location | Normalized claim | Verdict | Evidence |
|---|---|---|---|---|
| COMP-01 | ApiReference.astro:47-54, PR body |
The component renders an API-reference link. | Contradicted | The template contains no a, href, or route resolution. |
| COMP-02 | ApiReference.astro:15-44, PR body |
The prop is a fully qualified API identity that can be looked up. | Contradicted | The prop is an arbitrary string; the examples are simple names, and apiNames performs only a last-segment casing rewrite with no metadata lookup or validation. |
| COMP-03 | ApiReference.astro:25-44 |
The last dotted segment pivots between PascalCase and camelCase. | Verified | lastIndexOf('.') preserves the prefix and rewrites only the first leaf character. |
| COMP-04 | ApiReference.astro:84-88 |
C# is the fallback language when no component storage value exists. | Verified | apply(readStored()) sets data-aspire-lang to csharp when no value is found. |
| COMP-05 | ApiReference.astro:92-101 |
Pointer-clicking a Starlight aspire-lang tab updates the component language. |
Verified | The delegated click handler reads the Starlight storage key after the click. |
| COMP-06 | ApiReference.astro:103-106 |
A language change in another window updates the component. | Verified | The storage listener handles the Starlight storage key. |
| COMP-07 | ApiReference.astro:56-107 |
The component shares a guard and storage key with ContainerImages. |
Verified | Both components use window.__aspireLangPivot and starlight-synced-tabs__aspire-lang. |
| API-01 | add-aspire-existing-app.mdx:68, glossary.mdx, troubleshooting.mdx |
WithReference exists in C# and TypeScript. |
Verified | Aspire.Hosting.ResourceBuilderExtensions.WithReference; TS capability Aspire.Hosting/withReference. |
| API-02 | add-aspire-existing-app.mdx:68, glossary.mdx, troubleshooting.mdx |
WaitFor exists in C# and TypeScript. |
Verified | Aspire.Hosting.ResourceBuilderExtensions.WaitFor; TS capability Aspire.Hosting/waitFor. |
| API-03 | glossary.mdx |
WaitForCompletion exists in C# and TypeScript. |
Verified | C# WaitForCompletion; TS capability Aspire.Hosting/waitForResourceCompletion with display name waitForCompletion. |
| API-04 | glossary.mdx |
WaitForStart exists in C# and TypeScript. |
Verified | C# WaitForStart; TS capability Aspire.Hosting/waitForStart. |
| API-05 | app-host.mdx |
WithPersistentLifetime exists in C# and TypeScript. |
Verified | C# API surface and TS capability Aspire.Hosting/withPersistentLifetime. |
| API-06 | add-aspire-existing-app.mdx |
AddExecutable exists in C# and TypeScript. |
Verified | C# API surface and TS capability Aspire.Hosting/addExecutable. |
| API-07 | aspire-mcp-server.mdx |
ExcludeFromMcp exists in C# and TypeScript. |
Verified | C# API surface and TS capability Aspire.Hosting/excludeFromMcp. |
| API-08 | resource-mcp-servers.mdx |
The intended core WithMcpServer exists in C# and TypeScript. |
Verified | Core C# method and TS capability Aspire.Hosting/withMcpServer. A same-named Community Toolkit C# method exports as withInspectedMcpServer, demonstrating why FQN resolution is required. |
| API-09 | resource-mcp-servers.mdx |
The intended PostgreSQL WithPostgresMcp exists in C# and TypeScript. |
Verified | Aspire.Hosting.PostgresBuilderExtensions.WithPostgresMcp; TS capability Aspire.Hosting.PostgreSQL/withPostgresMcp. An Azure PostgreSQL API shares the simple name. |
| API-10 | app-host.mdx, faq.mdx |
AddUvicornApp exists in C# and TypeScript. |
Verified | PythonAppResourceBuilderExtensions.AddUvicornApp; TS capability Aspire.Hosting.Python/addUvicornApp. |
| API-11 | app-host.mdx |
WithUv exists in C# and TypeScript. |
Verified | C# [AspireExport]; TS capability Aspire.Hosting.Python/withUv. |
| API-12 | app-host.mdx, faq.mdx |
AddNodeApp exists in C# and TypeScript. |
Verified | C# JavaScript hosting API; TS capability Aspire.Hosting.JavaScript/addNodeApp. |
| API-13 | app-host.mdx |
WithNpm exists in C# and TypeScript. |
Verified | C# JavaScript hosting API; TS capability Aspire.Hosting.JavaScript/withNpm. Other packages also expose same-named functions, so package identity matters. |
| API-14 | deploy-first-app.mdx, faq.mdx |
AddViteApp exists in C# and TypeScript. |
Verified | C# JavaScript hosting API; TS capability Aspire.Hosting.JavaScript/addViteApp. |
| API-15 | deploy-first-app.mdx, faq.mdx |
AddJavaScriptApp exists in C# and TypeScript. |
Verified | C# JavaScript hosting API; TS capability Aspire.Hosting.JavaScript/addJavaScriptApp. |
| API-16 | faq.mdx:102 |
AddPythonApp exists as a current API. |
Verified with nuance | Both language APIs exist, but the C# overloads are [Obsolete] in PythonAppResourceBuilderExtensions.cs:179,221, recommending AddPythonScript, AddPythonModule, or AddPythonExecutable. |
| API-17 | faq.mdx:102 |
AddPythonModule exists in C# and TypeScript. |
Verified | C# method and TS capability Aspire.Hosting.Python/addPythonModule. |
| API-18 | glossary.mdx |
WithEnvironment exists in C# and TypeScript. |
Verified | Core C# overloads and TS capability Aspire.Hosting/withEnvironment. |
| API-19 | glossary.mdx |
AddPostgres is provided by Aspire.Hosting.PostgreSQL in both languages. |
Verified | Aspire.Hosting.PostgresBuilderExtensions.AddPostgres; TS capability Aspire.Hosting.PostgreSQL/addPostgres. |
| API-20 | glossary.mdx:403 |
Core IResourceBuilder<T>.WithAnnotation has a TypeScript counterpart named withAnnotation. |
Contradicted | No Aspire.Hosting/withAnnotation exists. The only generated TS withAnnotation is Aspire.Hosting.Kubernetes/withAnnotation, an incompatible KubernetesManifestResource method taking key and value. |
| API-21 | resource-mcp-servers.mdx:131 |
Calling WithMcpServer() uses the root path. |
Contradicted | McpServerResourceBuilderExtensions.WithMcpServer defaults path to /mcp. |
| API-22 | resource-mcp-servers.mdx |
WithMcpServer and WithPostgresMcp use diagnostics ASPIREMCP001 and ASPIREPOSTGRES001. |
Verified | Attributes in McpServerResourceBuilderExtensions.cs and PostgresBuilderExtensions.cs. |
Blocking source findings
- The component does not implement the promised lookup or link.
- Simple names cannot identify package/type/member identity and cannot honor explicit
AspireExportrenames. - The component invents a core TypeScript
withAnnotationAPI that does not exist. - The changed MCP page states the wrong default path.
- The changed FAQ foregrounds an obsolete C# API without noting its replacements.
Phase B - Doc-tester results
Documentation Test Report
Focus Area: PR #1588 ApiReference behavior on the eight changed get-started routes
Date: 2026-09-01
Tester: doc-tester agent
Served ref: e24dac84f3c54cadeb88c0224b381da578d32e2f
Local URL: http://localhost:4321/
Summary
| Category | Passed | Failed | Warnings |
|---|---|---|---|
| Changed routes load | 8 | 0 | 0 |
| API-reference rendering | 23 | 20 | 0 |
| API-reference links | 0 | 43 | 0 |
| Language synchronization paths | 2 | 2 | 0 |
| Code examples / CLI commands | N/A | N/A | 0 |
Critical Issues
Issue 1: API references do not link to API documentation
Location: All eight changed routes
Type: Link
Severity: Critical
What the pull request promises:
Render a link to the API reference documentation.
What actually happens:
Browser inspection found 43
.api-referenceinstances and zero descendant links. None of the rendered API names can be followed to either the C# or TypeScript API reference.
Evidence:
The changed routes contained 43 component instances in total; querySelectorAll('.api-reference a') returned zero links on every route.
Recommended Action:
- Resolve each fully qualified API identifier against the generated reference data.
- Render a canonical C# or TypeScript API-reference link for the active language.
Issue 2: Inline references break paragraph markup and disappear
Location: Seven of the eight changed routes
Type: Component rendering / accessibility
Severity: Critical
What the documentation intends:
API names should read as inline code within prose.
What actually happens:
The component places Starlight's block-level
Codeoutput inside<span>elements. The browser repairs that invalid inline structure by moving the generated<div class="expressive-code">and<figure>outside the component. Twenty of 43.api-referenceelements are empty at their intended inline position, and the same 20 API names become detached code blocks with visible copy controls.
Evidence:
On /get-started/app-host/, the sentence "To also keep the container running ... add" ends before the API name. WithPersistentLifetime() appears as a separate figure with a "Copied WithPersistentLifetime() to clipboard" button, followed by the remainder of the sentence. The same failure occurs 20 times across the changed routes.
Recommended Action:
- Render valid phrasing content, such as an
<a><code>...</code></a>structure, instead of embedding a block code component inside<span>. - Add browser coverage that asserts each reference remains inside its containing paragraph or list item and does not expose a copy button.
Issue 3: Keyboard and PivotSelector language changes leave API names stale
Location: /get-started/resource-mcp-servers/ and /get-started/deploy-first-app/
Type: Component behavior / accessibility
Severity: Critical
What the component claims:
The API name pivots with the page's C#/TypeScript language selection.
What actually happens:
A pointer click on a Starlight TypeScript tab updates the API name, but two other supported language-change paths do not. Clicking the page's
PivotSelectorchanges the URL and both local-storage keys to TypeScript whiledata-aspire-langand the visible API name remain C#. Keyboard-selecting the TypeScript Starlight tab changes the selected tab, URL, local storage, and canonicaldata-apphost-langvalue, butdata-aspire-langremains C# and the API names remain PascalCase.
Evidence:
After clicking the TypeScript PivotSelector, the URL was ?aspire-lang=typescript and storage contained aspire-lang=typescript plus starlight-synced-tabs__aspire-lang=TypeScript, while both rendered attributes remained C# and AddJavaScriptApp stayed visible. After pressing ArrowRight on the C# Starlight tab, the selected tab and data-apphost-lang became TypeScript while data-aspire-lang and WithMcpServer() stayed C#.
Recommended Action:
- Consume the site's canonical
data-apphost-langstate instead of registering a second global state attribute and partial event listener. - Cover pointer, keyboard,
PivotSelector, query-string initialization, and persisted initialization.
Warnings
Warning 1: Simple names provide no authoring or reader disambiguation
Location: All changed <ApiReference name="..."> usages
Issue: The rendered text provides no package, declaring type, overload, or destination. A new user cannot determine which WithReference, WithMcpServer, or similarly named API is intended.
Suggestion: Require a fully qualified identifier and validate it against the generated API data, while displaying the short language-appropriate label.
Passed Checks
- All eight affected routes loaded successfully from the PR head.
- A full navigation with
?aspire-lang=csharpor?aspire-lang=typescriptinitialized non-empty references to the requested language. - Clicking a Starlight TypeScript tab with a pointer updated non-empty API references.
- Reloading after a
PivotSelectorchange restored the TypeScript API labels from persisted state. - No component-specific browser warnings were emitted.
Knowledge Gap: API identity
What I needed to know: Which package, declaring type, overload family, and language-specific API page each short name refers to.
Source of my knowledge: The rendered documentation did not provide this information.
User impact: New users cannot use the reference as a path to learn the API or distinguish same-named APIs.
Recommendation:
- Make the component's link and fully qualified authoring identifier the source of this context.
Recommendations
- Priority fixes: Correct the invalid inline markup, render real API links from fully qualified identifiers, and use the canonical language state.
- Documentation gaps: Migrate every changed usage to an exact FQN and keep the displayed label concise.
- Product issues: Add resolver, component, validator, and browser tests; a static string-inclusion test cannot detect any of the critical failures above.
|
|
||
| interface Props { | ||
| /** | ||
| * Fully qualified Aspire API name, e.g. `AddPostgres`, `addPostgres`, |
There was a problem hiding this comment.
These examples are not fully qualified API identities, and the prop is still an unvalidated free-form string. Please make this accept a canonical FQN (for example, Aspire.Hosting.PostgresBuilderExtensions.AddPostgres) and resolve both language labels and canonical hrefs from the generated C# and TypeScript metadata. Simple names cannot do that reliably: this data set contains 29 distinct declaring-type/package candidates for WithReference, and AspireExport can explicitly rename the TypeScript capability. For unresolved or ambiguous input, the component can render unlinked code plus a warning, but a separate authoring validator should fail with the source file, line, FQN, and candidate matches.
There was a problem hiding this comment.
Fixed.
|
|
||
| <span class="api-reference not-content"> | ||
| <span class="ar-lang" data-lang="csharp"> | ||
| <Code code={csharp} lang="csharp" frame="none" showLineNumbers={false} /> |
There was a problem hiding this comment.
Starlight Code emits block markup (div > figure > pre) here, so placing it inside these spans is not valid inline prose. The browser reparents that markup: across the eight changed routes, 20 of 43 .api-reference elements were empty at their intended position, and the same 20 names became detached code blocks with visible copy controls. Please render valid phrasing content such as <a><code>...</code></a> and add a browser assertion that the reference remains inside its containing paragraph/list item.
There was a problem hiding this comment.
Fixed.
| } | ||
|
|
||
| function apply(lang) { | ||
| document.documentElement.dataset.aspireLang = lang || 'csharp'; |
There was a problem hiding this comment.
This creates a second language-state attribute instead of consuming the existing data-apphost-lang state. It only stays current for pointer clicks that match this component selector. In the rendered PR, clicking the TypeScript PivotSelector updated the URL and both storage keys while data-aspire-lang and the API label stayed C#; keyboard-selecting the TypeScript Starlight tab updated data-apphost-lang but left data-aspire-lang and the API label in C#. Please bind to the canonical state and remove this duplicate listener path.
There was a problem hiding this comment.
Fixed.
| name: 'ApiReference renders both C# and TypeScript casings and the language pivot script', | ||
| Component: ApiReference, | ||
| props: { name: 'AddPostgres()' }, | ||
| includes: [ |
There was a problem hiding this comment.
These substring checks all pass while the rendered component has no links, 20 usages disappear from inline prose, detached copy controls are exposed, and keyboard/PivotSelector changes leave labels stale. Please split the lookup into a pure resolver with tests for exact FQNs, overload families, export renames, ambiguity, missing symbols, and missing counterparts; then add parsed-DOM/browser coverage for inline structure, hrefs, and every supported language-change path. The authoring validator also needs focused typo/ambiguity diagnostics tests.
| |------|-------------| | ||
| | `IResourceAnnotation` | Typed metadata object attached to resources. | | ||
| | `WithAnnotation()`/`withAnnotation()` | Fluent method to attach typed annotations. | | ||
| | <ApiReference name="WithAnnotation()" /> | Fluent method to attach typed annotations. | |
There was a problem hiding this comment.
The casing heuristic invents a core TypeScript withAnnotation() here. There is no Aspire.Hosting/withAnnotation entry in the generated TypeScript API data. The only exported TypeScript namesake is Aspire.Hosting.Kubernetes/withAnnotation, an incompatible KubernetesManifestResource.withAnnotation(key, value) API. A metadata-backed resolver/validator should reject this usage rather than presenting a different API to TypeScript readers.
There was a problem hiding this comment.
Fixed.
|
|
||
| - `WithMcpServer()` — uses the default HTTP endpoint at the root path | ||
| - `WithMcpServer("/mcp")` — uses the default HTTP endpoint at `/mcp` | ||
| - <ApiReference name="WithMcpServer()" /> — uses the default HTTP endpoint at the root path |
There was a problem hiding this comment.
The no-argument default is /mcp, not the root path. McpServerResourceBuilderExtensions.WithMcpServer declares string? path = "/mcp" on microsoft/aspire main. Please correct this bullet (and avoid presenting the explicit "/mcp" call as behavior distinct from the default). Source: https://github.com/microsoft/aspire/blob/4a8b541c973baeb271f244603b0289c3f47933cf/src/Aspire.Hosting/McpServerResourceBuilderExtensions.cs#L43-L46
There was a problem hiding this comment.
Fixed.
| - **C# / .NET** — First-class project references, service defaults, and integrations | ||
| - **Python** — `AddPythonApp`, `AddUvicornApp`, `AddPythonModule` with uv/pip/venv package management and automatic Dockerfile generation | ||
| - **JavaScript / TypeScript** — `AddJavaScriptApp`, `AddViteApp`, `AddNodeApp` with npm/yarn/pnpm auto-detection | ||
| - **Python** — <ApiReference name="AddPythonApp" />, <ApiReference name="AddUvicornApp" />, <ApiReference name="AddPythonModule" /> with uv/pip/venv package management and automatic Dockerfile generation |
There was a problem hiding this comment.
AddPythonApp exists, but both C# overloads are obsolete and direct users to AddPythonScript, AddPythonModule, or AddPythonExecutable with .WithArgs(...). This FAQ now foregrounds it as a current API without that caveat. Please replace it with the recommended APIs or clearly surface the deprecation. Source: https://github.com/microsoft/aspire/blob/4a8b541c973baeb271f244603b0289c3f47933cf/src/Aspire.Hosting.Python/PythonAppResourceBuilderExtensions.cs#L179-L181
There was a problem hiding this comment.
According to my reading of the referred code, the first overload of AddPythonApp() is not obsolete. Two overloads are obsolete, but AddPythonApp(IDistributedApplicationBuilder, string, string, string) remains.
Summary
The PR adds a new Astro component named to render the correct API names, depending on the user's preference for C#/TypeScript. The component:
tag with the correct name.This PR also includes unit tests for the new component and applies the new component, initially to all pages in the get-started folder.
Third-party links and affiliations
None
Validation
pnpm run devand manually observed API names in get-started pages.