Skip to content

Create an API Reference component - #1588

Draft
Alistair Matthews (alistairmatthews) wants to merge 8 commits into
microsoft:mainfrom
alistairmatthews:create-api-reference-component
Draft

Create an API Reference component#1588
Alistair Matthews (alistairmatthews) wants to merge 8 commits into
microsoft:mainfrom
alistairmatthews:create-api-reference-component

Conversation

@alistairmatthews

@alistairmatthews Alistair Matthews (alistairmatthews) commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds an inline <ApiReference /> Astro component that resolves a canonical C# fully qualified member name and renders the corresponding C# or TypeScript API name and reference link using the site's existing AppHost language preference.

<ApiReference name="Aspire.Hosting.PostgresBuilderExtensions.AddPostgres" />

When multiple packages declare the same FQN, authors can disambiguate it with the optional package prop:

<ApiReference
  name="Aspire.Hosting.JavaScriptHostingExtensions.WithNpm"
  package="Aspire.Hosting.JavaScript"
/>

What changed

  • Replaces hard-coded API names across the Get started documentation with linked API references.
  • Builds a shared index from the generated C# and TypeScript catalogs instead of scanning every catalog for every component instance.
  • Reuses one index for the production process and one per development request, with cached O(1) resolutions for repeated references.
  • Maps TypeScript APIs from generated export metadata, including renamed exports, overload dispatchers, extension-method receiver types, type-level exports, and cross-module core dispatchers.
  • Reports ambiguous or invalid C# references with actionable candidates and renders APIs without a TypeScript counterpart as canonical, unlinked code instead of inventing a link.
  • Validates authored <ApiReference /> usage through the MDX AST, including JSX inside expressions and exports, while ignoring examples in code fences and comments.
  • Preserves valid inline phrasing markup and the existing CSS-only language switch. The component adds no client-side JavaScript or hydration.

Performance

The current generated catalogs produce 8,062 indexed API entries. In a local benchmark, building the complete index took approximately 88 ms, while one million repeated resolutions took approximately 16 ms after initialization.

The browser receives only static HTML and CSS, so adding hundreds of references across the documentation does not add client-side listeners, catalog data, or lookup work.

Validation

  • Runs the API-reference, custom-component, and site-tour unit suites together: 92 tests.
  • Resolves every authored API reference against the real generated catalogs.
  • Checks that explicitly exported C# members have a generated TypeScript route.
  • Covers linked, unlinked, ambiguous, renamed, overloaded, generic, receiver-targeted, and cross-module APIs.
  • Includes component prop contracts and responsive Chromium coverage.

Third-party links and affiliations

None

@IEvangelist

Copy link
Copy Markdown
Member

Feel free to continue to work on this, but do not merge until I review please.

@IEvangelist David Pine (IEvangelist) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

  1. 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 AspireExport identities and renames.
  2. 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.
  3. Add a separate authoring validator that fails tests/CI for unresolved or ambiguous usages and reports the file, line, supplied FQN, and useful candidates.
  4. Consume the existing canonical AppHost language state instead of maintaining a second attribute/listener implementation.
  5. 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 verification

PR: 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

  1. The component does not implement the promised lookup or link.
  2. Simple names cannot identify package/type/member identity and cannot honor explicit AspireExport renames.
  3. The component invents a core TypeScript withAnnotation API that does not exist.
  4. The changed MCP page states the wrong default path.
  5. 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-reference instances 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 Code output 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-reference elements 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 PivotSelector changes the URL and both local-storage keys to TypeScript while data-aspire-lang and the visible API name remain C#. Keyboard-selecting the TypeScript Starlight tab changes the selected tab, URL, local storage, and canonical data-apphost-lang value, but data-aspire-lang remains 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-lang state 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=csharp or ?aspire-lang=typescript initialized non-empty references to the requested language.
  • Clicking a Starlight TypeScript tab with a pointer updated non-empty API references.
  • Reloading after a PivotSelector change 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

  1. Priority fixes: Correct the invalid inline markup, render real API links from fully qualified identifiers, and use the canonical language state.
  2. Documentation gaps: Migrate every changed usage to an exact FQN and keep the displayed label concise.
  3. Product issues: Add resolver, component, validator, and browser tests; a static string-inclusion test cannot detect any of the critical failures above.

Comment thread src/frontend/src/components/ApiReference.astro Outdated
Comment thread src/frontend/src/components/ApiReference.astro Outdated
Comment thread src/frontend/src/components/ApiReference.astro Outdated
name: 'ApiReference renders both C# and TypeScript casings and the language pivot script',
Component: ApiReference,
props: { name: 'AddPostgres()' },
includes: [

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/frontend/src/content/docs/get-started/glossary.mdx Outdated
Comment thread src/frontend/src/content/docs/get-started/resource-mcp-servers.mdx Outdated
Comment thread src/frontend/src/content/docs/get-started/faq.mdx Outdated
Build and cache indexed C# and TypeScript API metadata, validate authored references, and support package-qualified ambiguous names.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants