Skip to content

Add JavaScript code for non-AppHost examples - #1845

Merged
David Pine (IEvangelist) merged 6 commits into
microsoft:copilot/add-docs-for-app-model-apifrom
alistairmatthews:alistairmatthews/add-docs-for-app-model-api
Oct 13, 2026
Merged

David Pine (IEvangelist) merged 6 commits into
microsoft:copilot/add-docs-for-app-model-apifrom
alistairmatthews:alistairmatthews/add-docs-for-app-model-api

Conversation

@alistairmatthews

@alistairmatthews Alistair Matthews (alistairmatthews) commented Oct 8, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

AppHost examples in copilot/add-docs-for-app-model currently include both C# and typescript in tabs. However, examples for executables, worker services, and web services include only .NET C# code. This PR adds JavaScript equivalent examples for all C# examples.

NOTE: This PR targets copilot/add-docs-for-app-model, not main. PR #454

Third-party links and affiliations

None

Validation

  • Test suite run in full: pnpm test
  • I asked Claude to test the JavaScript examples - it compiled and ran them successfully.

@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.

Docs accuracy review

Verdict: COMMENT. The reported tab confusion is reproducible, even though all four application/API-language and AppHost-language combinations render correctly within each page.

Phase A: microsoft/aspire, main, SHA 26b5ca7a51dd572d478316df8cf96755820754a5; the matching stacked target branch does not exist in the product repository, so the documented main fallback was used. 59 distinct catalog groups: 38 verified, 3 verified-with-nuance, 0 contradicted, 0 unverifiable, 18 narrative/UI/external-platform context groups. The full audited catalog and pinned evidence follow.

Phase B: Independent, source-blind rendered/runtime walkthrough of /app-host/console-apps/ and /app-host/cors-configuration/ at the PR head: 0 critical issues, 2 usability warnings. Feasible literal examples passed; full Aspire orchestration and other unexecuted outcomes are explicitly not claimed as tested.

The two actionable reader barriers are (1) an unexplained installation-method/target-file axis that can show C# installation instructions above a selected TypeScript AppHost, and (2) a shared persisted app-language preference that silently changes a Node.js worker walkthrough after selecting a .NET API on the other page. Please make the applicable installation path explicit and separate or clearly explain the API-versus-worker preference. This is not a request to remove the correct support for mixed-language AppHosts and applications.

Phase A - Claim verification

Reviewed both changed files at PR head cdaf11423ac63c35c408b5fc86735af5ed1553be, against the PR's actual target copilot/add-docs-for-app-model-api (this is a stacked PR, not a PR targeting main).

Source: microsoft/aspire, branch main, SHA 26b5ca7a51dd572d478316df8cf96755820754a5. The corresponding copilot/add-docs-for-app-model-api source branch does not exist (GitHub branch lookup returned HTTP 404), so the documented fallback to main was used. Every source read was pinned to this SHA; no evidence from other product branches was substituted. There are no Community Toolkit or Azure SDK API additions requiring those repositories.

Catalog: 59 distinct claim groups, deduplicating repeated API calls across sample blocks: 38 verified, 3 verified-with-nuance, 0 contradicted, 0 unverifiable, 18 narrative/context groups. The latter include documentation UI behavior, web-platform context, and external Node/Express/ioredis/OpenTelemetry example behavior; they are not claims that Aspire implements those third-party APIs. Their literal walkthrough and runtime checks belong to the independent Phase B report below. No current-release Aspire version literals were added that require version placeholders.

The source check found no contradicted Aspire API or environment-variable claims. Three qualifications are worth retaining:

  • A16, console-apps.mdx:355: "withReference(catalog) ... as CATALOG_HTTPS and CATALOG_HTTP" applies to the endpoints actually configured on catalog. The reference callback iterates existing, non-excluded endpoints; it does not create both schemes. The sample correctly uses a fallback. "For each configured endpoint" would make the prose equally precise.
  • A22, console-apps.mdx:364: "Background workers run for as long as the AppHost runs" describes the intended application-lifetime worker pattern, not a guarantee that a worker cannot exit/fail early or a statement about explicitly persistent resources.
  • A23, console-apps.mdx:452: "then closes its Redis connection" describes the graceful path through the sample's signal handlers and finally. The orchestrator can escalate to forceful termination; cleanup is not guaranteed after a forced kill. The loop delays ten seconds after doWork, rather than promising a fixed-rate ten-second schedule. Prefer wording that identifies graceful shutdown.
Auditable claim catalog: verified and verified-with-nuance Aspire claims

Locations below are in src/frontend/src/content/docs/app-host/. Repeated calls in the same new sample family are one claim group; both AppHost languages were checked.

ID PR location Claim Verdict Source evidence
A03 console-apps.mdx:33 C# project metadata and TypeScript explicit project paths Verified Project overloads
A04 console-apps.mdx:34 AppHost-relative app directory; app-relative Node script Verified Node registration
A05 console-apps.mdx:93-95; cors-configuration.mdx:231-233 Aspire.Hosting.JavaScript owns the hosting APIs Verified Hosting package, Node registration, JavaScript registration
A06 console-apps.mdx:108,319,560,659; cors-configuration.mdx:340 C# AddNodeApp(name, appDirectory, scriptPath) Verified Node registration
A07 Corresponding new TypeScript Node blocks addNodeApp arguments and awaitability Verified Polyglot integration sample, thenable generator
A08 New TypeScript imports ./.aspire/modules/aspire.mjs exports createBuilder Verified Real starter template, builder export
A09 New TypeScript AppHost blocks Fluent and individual awaited calls; build().run() Verified Thenable generator, real starter template
A10 New cache AppHost blocks AddRedis / addRedis resource creation Verified Redis registration
A11 New cache references Node resource supports WithReference(cache) Verified Reference dispatcher, connection injection
A12 console-apps.mdx:135 Detect package.json, install dependencies, wait before startup Verified Package detection, npm setup, installer ordering
A13 console-apps.mdx:159 CACHE_URI, CACHE_HOST, CACHE_PORT, CACHE_PASSWORD, ConnectionStrings__cache Verified Connection injection, Redis properties, default password
A14 console-apps.mdx:162-165 AddJavaScriptApp runs a named package-manager script Verified JavaScript registration, script launch
A15 console-apps.mdx:283 Node apps consume injected endpoints rather than a .NET HTTP-client resolver Verified Endpoint injection, Node defaults
A16 console-apps.mdx:355 CATALOG_HTTP / CATALOG_HTTPS Verified with nuance Only actual included endpoints are injected
A17 console-apps.mdx:355 services__catalog__https__0 format Verified Indexed service-discovery injection
A18 console-apps.mdx:317,334-337 Node consumer can reference a .NET catalog project Verified Project overloads
A19 console-apps.mdx:320,345 WithReference(catalog) / withReference(catalog) Verified Service reference, reference dispatcher
A20 New catalog and database wait calls WaitFor / waitFor supports readiness ordering Verified Wait implementation and export
A21 console-apps.mdx:357,728,747 Node SDK/exporter must be configured to send telemetry Verified OTLP configuration supplies environment, not emitters, receiver
A22 console-apps.mdx:364 Application-lifetime workers are cleaned up on AppHost shutdown Verified with nuance Full orchestrator cleanup
A23 console-apps.mdx:452 Redis cleanup on worker shutdown Verified with nuance Graceful shutdown can escalate
A24 New worker database AppHost blocks AddPostgres / AddDatabase and TypeScript equivalents Verified Postgres registration, database registration
A25 New worker WithReference(db) calls Database reference supplies connection information, not database processing Verified Connection injection; sample explicitly leaves doWork unimplemented
A26 console-apps.mdx:595 APP_DB_URI exists for the app-db database reference Verified Connection injection, database URI
A27 console-apps.mdx:595 Dash becomes underscore in connection-property variable prefix Verified Name encoder, uppercase prefix
A28 New explicit-start Node blocks WithExplicitStart / withExplicitStart API Verified Explicit-start annotation
A29 console-apps.mdx:683 Explicit-start resource stays manually/programmatically startable Verified Explicit-start contract, start operation
A30 console-apps.mdx:688-693 Console logs does not forward stdin to Node readline Verified Output-only resource log streams, log-viewer UI
A31 console-apps.mdx:728 Normal Node stdout/stderr appear in Console logs Verified Runtime stdout/stderr streams, log-viewer UI
A32 console-apps.mdx:747 Structured logs require export, not console output alone Verified OTLP log receiver, exporter configuration
A33 console-apps.mdx:754,785-796 Node can read literal ConnectionStrings__cache Verified Connection injection
A34 console-apps.mdx:798 Redis connection string differs from the separate URI property Verified Redis connection syntax, URI/properties
A35 cors-configuration.mdx:336-337,355-359 C#/TypeScript frontend dev package-script registration Verified JavaScript registration, polyglot script options
A36 cors-configuration.mdx:337,341,359,362 HTTP endpoint port and env options Verified HTTP endpoint signature, starter usage
A37 cors-configuration.mdx:329,341-342,362-363 API receives assigned listening port through PORT Verified Endpoint creation/env wiring, HTTP delegation
A38 cors-configuration.mdx:342,363,376 WithReference(frontend) injects FRONTEND_HTTP Verified Endpoint-name variables
A39 cors-configuration.mdx:343,364 Mark HTTP/HTTPS endpoints external Verified External endpoint API
A40 cors-configuration.mdx:372 Development AppHost sets Node NODE_ENV=development Verified Node defaults
A41 cors-configuration.mdx:401 Same frontend URL also in services__frontend__http__0 Verified Indexed service-discovery injection
A42 cors-configuration.mdx:406-409 Consumer-network endpoint is not necessarily public production origin Verified Endpoint network context and host rewriting
A43 Added integration / telemetry / reference links Corresponding documentation resources exist in the pinned PR tree Verified PR-head docs tree; checked the JavaScript, Redis connect, Postgres connect, resource-naming, service-discovery and add-existing-app paths, not a general broken-link audit
Remaining catalog groups: documentation UI and external application/platform context
  • A01-A02: Runtime/AppHost tab-axis descriptions and synchronized selections (console-apps.mdx:24-29; cors-configuration.mdx:38-41).
  • A44: External ioredis package, ES-module manifest, Redis set/get/quit (console-apps.mdx:137-157,438-450).
  • A45: Native Node fetch, dependency-free manifest, HTTPS/HTTP fallback (console-apps.mdx:287-306).
  • A46: Node signals, AbortController, abortable timer and graceful sample control flow (console-apps.mdx:455-484).
  • A47: External OpenTelemetry Logs API and logger.emit fragment (console-apps.mdx:731-745).
  • A48: Literal Node process.env lookup (console-apps.mdx:785).
  • A49: Browser CORS versus server-to-server calls and the origin tuple (cors-configuration.mdx:14,24,26).
  • A50: External Express/cors versions, development allowlist and middleware defaults (cors-configuration.mdx:77-109).
  • A51: Wildcard-origin production caution (cors-configuration.mdx:115-117).
  • A52-A53: The sample's application-defined CORS_ALLOWED_ORIGINS variable, empty list, comma parsing and environment examples (cors-configuration.mdx:186-224).
  • A54: Application-authored development URL / production allowlist branch (cors-configuration.mdx:378-399).
  • A55-A56: External Express middleware ordering and implicit versus CORS OPTIONS responses (cors-configuration.mdx:445-460,484-488).
  • A57-A58: Browser/client credentials, bearer headers and wildcard credential restrictions (cors-configuration.mdx:514-543).
  • A59: External Express/cors further-reading reference (cors-configuration.mdx:587).

These were retained rather than silently omitted from the catalog, but not misattributed to an Aspire-owned implementation. Phase B's blind-user findings and executable-example coverage are preserved verbatim below.

Phase B - Independent doc-tester results

Documentation Test Report

Focus Area: PR #1845 — console/worker apps and CORS configuration; source-blind new-reader walkthrough of language, package-install and environment selections, plus literal feasible examples.
Date: 2026-10-08
Tester: doc-tester agent

Summary

Category Passed Failed Warnings
Content Accuracy 10 0 2
Code Examples 6 0 0
CLI Commands 2 0 0
Links 4 0 0

Counts are logical test groups enumerated under Passed Checks, not the number of screenshots, API claims or individual HTTP requests. Runtime evidence includes 28 asserted HTTP/preflight results, nine actual browser request outcomes, and two Node.js service-discovery-script outcomes. Full Aspire orchestration, .NET sample compilation, production deployment, real cookie delivery and dashboard configuration remain explicitly unverified.

Overall: No critical issue or documented-example failure was reproduced. Both pages' application/API-language and AppHost-language axes actually synchronize independently within each page. Two concrete usability warnings remain: the package-installation axis is not covered by the page's “two sets of tabs” explanation and offers C# file choices alongside a TypeScript AppHost; and the two pages silently share one application-language preference despite selecting different application roles.

Critical Issues

None reproduced in the scoped tests.

This is not an assertion that all API claims or every full application example are correct. The unexecuted outcomes below remain unverified and must not be converted to passes. The earlier unavailable preview has been resolved and does not contribute to the final result.


Warnings

Warning 1: The “two sets of tabs” explanation omits a third synchronized choice that can show C# installation next to TypeScript AppHost code

Location:

  • http://127.0.0.1:4321/app-host/console-apps/, Adding a console app to the AppHost and Adding to the AppHost. PR-head console-apps.mdx:24–29,38,93–99,492–498.
  • http://127.0.0.1:4321/app-host/cors-configuration/, Using Aspire to inject the frontend URL locally. PR-head cors-configuration.mdx:38–43,232–243.

Issue: The prose explains two independent sets:

“The examples use two sets of tabs, which you choose independently”

and:

“C# and TypeScript tabs select the language of the AppHost itself.”

The rendered installation component adds another synchronized set:

“Aspire CLI” / “apphost.cs (C# file-based app)” / “PackageReference (*.csproj)”

With Node.js and TypeScript selected, choosing the second install option leaves TypeScript selected but presents:

C# — AppHost.cs
#:package Aspire.Hosting.JavaScript@*

immediately above:

TypeScript
apphost.mts
import { createBuilder } from './.aspire/modules/aspire.mjs';

Choosing the project-file option similarly presents:

<PackageReference Include="Aspire.Hosting.JavaScript" Version="*" />

while the AppHost remains TypeScript. On the console page, choosing an install format also changes the other hosting-install blocks, including Redis and PostgreSQL. It does not change the AppHost-language selection.

Reproduction: Select Node.js; select TypeScript in the AppHost block; choose apphost.cs (C# file-based app) in a hosting-install block; inspect the next AppHost block; then choose PackageReference (*.csproj). Repeat in the CORS injection section.

Actual result: The same procedure shows a C# file/package instruction and a TypeScript AppHost without explaining that the install choices form a separate axis, or which installation route to use when the reader only has apphost.mts.

Expected reader guidance: Explain the package-install choices and their target files separately from app language and AppHost language. A TypeScript AppHost reader should not have to infer what to do with a displayed C# directive or .csproj fragment.

Evidence: console-three-choice-axes.png visibly contains the JavaScript hosting C# package directive directly above the selected TypeScript AppHost code. Snapshots console-node-typescript-filebased-install.yaml, console-node-typescript-csproj-install.yaml, console-final-three-axes.yaml, cors-typescript-filebased-package.yaml and cors-typescript-csproj-package.yaml preserve exact choices and selected states on both pages. cors-typescript-csharp-install-choice.png captures the CORS TypeScript/Node.js code context after that selection, but the installation control is above its viewport; the CORS installer combination is established by the snapshots, not by that screenshot alone.

Suggestion: Add a concrete selection rule near the package instructions, for example that the CLI route is the route to follow for the TypeScript AppHost walkthrough, while the C# file-based and project-file options target those respective C# AppHost formats. Label the installation axis as Install method / target file, rather than leaving the reader to treat these as additional language or platform choices. Alternatively hide or explain inapplicable format choices for the selected AppHost. Verify the intended rule before authoring it.

Classification: Actionable comprehension barrier, not a demonstrated syntax/API defect. No instruction explicitly told the reader to paste a C# directive into TypeScript, so this is a warning, not a fabricated copy/paste failure.


Warning 2: Changing the API-language choice also changes the console-app choice on the other page

Location: Both affected routes; console-app selection versus API selection. PR-head console-apps.mdx:26–29,40,175,368,498,611,710,756; cors-configuration.mdx:40–43,124,238,419,469,498.

Issue: The pages give the same labels different roles:

Console page: “tabs select the language of the console app or executable that the AppHost orchestrates.”

CORS page: “tabs select the language of the API that the AppHost orchestrates.”

Their selections are nevertheless shared between pages. This matters for the concrete mixed-language case of a Node.js worker calling a .NET API: changing the API selection erases the worker selection on return.

Reproduction:

  1. On console apps, choose Node.js with a C# AppHost.
  2. Expand AppHost → Configuration and click the actual CORS configuration sidebar link.
  3. Observe Node.js already selected for the API.
  4. Change the API selection to .NET while keeping C# AppHost selected.
  5. Expand AppHost → Resource types and click Console and worker apps.

Actual result: The console page now selects .NET throughout, although the last console-app choice was Node.js. C# AppHost remains selected correctly. The page gives no notice that changing API language also changes the console/worker language preference.

Expected reader guidance: Either keep the distinct API/console roles independently selectable across navigation, or explicitly explain the shared site-wide application-language preference and that mixed-language readers will need to reselect it on each page.

Evidence: console-node-csharp.yaml and console-nav-expanded.yaml record the Node.js console choice. cors-from-console-node-csharp.yaml records its inheritance as Node.js API. cors-dotnet-return-nav.yaml records the .NET API selection. console-after-changing-api-to-dotnet.yaml records the unexpected .NET console selection on return. observed-tab-storage.txt records the shared starlight-synced-tabs__console-app-lang preference; the two changed MDX documents both use that same sync key.

Suggestion: Use distinct preference groups for API language and console/worker language, retaining synchronization within each page and the independent shared AppHost preference; or show a clear global-preference explanation. This is not a failure of the C#/TypeScript axis or an inconsistent selected state within either individual page.


Passed Checks

Content Accuracy — 10 logical checks

  1. Rendered explanations distinguish application/API language from AppHost language. The changed prose is actually visible, not merely present in MDX. It explains Node.js + C# explicitly on console apps. C# client Program.cs under .NET + TypeScript is a .NET application sample, not evidence that the TypeScript AppHost selection is broken.
  2. Console-page four-way matrix works. .NET/C#, .NET/TypeScript, Node.js/TypeScript and Node.js/C# all display the corresponding AddProject/addProject or AddNodeApp/addNodeApp block and the appropriate AppHost filename. All visible app-language groups agree.
  3. Changing console-app language while keeping TypeScript fixed works. .NET → Node.js retains TypeScript. Selecting Node.js from the lower service-discovery group also selects Node.js in the adding/worker/explicit-start/logging/connection sections and retains TypeScript.
  4. Changing console-app language while keeping C# fixed works. Node.js → .NET → Node.js retains C#. Hidden nested AppHost groups do not revert to their initial language when exposed.
  5. CORS-page four-way matrix works. Node.js/C#, Node.js/TypeScript, .NET/TypeScript and .NET/C# all show the correct API snippets plus the corresponding AppHost block. App-language changes do not reset the AppHost-language choice.
  6. Environment selections are independent and retain state through API changes. Choosing Production under Node.js displays CORS_ALLOWED_ORIGINS=https://myapp.com,https://www.myapp.com; changing to .NET retains Production and displays appsettings.json with public origins. Returning to Node.js retains Production; selecting Development displays the localhost-origin setting.
  7. All three hosting-install formats are selectable and synchronize consistently. CLI, C# file-based and .csproj choices were exercised on both routes. The active panels and selected states agree. The usability issue in Warning 1 concerns explaining applicability, not a control that fails to switch.
  8. All three .NET client-package formats are selectable. .NET CLI, Program.cs (C# file-based app) and PackageReference (*.csproj) were exercised in the console service-discovery section and returned to .NET CLI. The displayed targets remain client-project targets, rather than AppHost package targets.
  9. Prerequisites and placeholders are explicit in the rendered context. Existing AppHost/shared service defaults, .NET project references, Node.js package.json/folder paths, Redis container runtime, frontend dev script/PORT requirement, worker placeholder processing, unused database-reference caveat, and logging exporter prerequisites are present. No setup was silently invented to replace these. The changed MDX does not hard-code a current Aspire release value; fixed third-party package ranges and the minimum .NET version were not treated as placeholder defects.
  10. CORS context, development caution and diagnostic fragment scope are visible. The page distinguishes browser-origin checks from server-to-server calls, warns against permissive production origins, scopes injected endpoints to local development, requires public production origins, marks the dashboard wildcard development-only, and states the authentication fragments' existing-service/function prerequisites. Actual standalone CORS execution supports the specific Node examples tested below; the untested .NET/dashboard behaviors are not inferred.

Code Examples — 6 logical checks

  1. Complete fixed-origin Express example runs literally. The api/package.json and api/server.js were copied through their actual rendered copy buttons, with imports, line breaks and comments intact. The unchanged server starts on its documented default port 5000, returns 200 OK, and supplies the specified localhost origin. Allowed preflight returns 204 and includes permitted methods/requested headers. A real browser at http://localhost:3000 reads the response; a browser at http://127.0.0.1:3000 cannot, because that is a different origin. A nonmatching fixed allow-origin header is not incorrectly counted as permission for the requester's origin.
  2. Complete environment-based Express example runs literally. With no CORS_ALLOWED_ORIGINS, GET still returns 200 to the HTTP client but lacks an allow-origin header; a real cross-origin browser fetch fails. The exact documented Development and Production strings allow their listed origins and omit permission for others. The localhost browser can read Development and cannot read the public-only Production configuration. Preflight behavior was asserted too.
  3. Complete injected-endpoint Express example runs literally under controlled documented environment values. With NODE_ENV=development and a FRONTEND_HTTP fixture value, it extracts the origin and permits that origin. With Production and the documented public allowlist, it permits public origins and ignores the local injected endpoint. With the endpoint missing and no fallback list, the cross-origin browser request is blocked. This verifies the API script's handling of the values, not that Aspire actually injects them.
  4. Credential-policy fragment works when inserted as instructed into the existing API policy. Replacing the fixed-origin CORS middleware with the copied credentials fragment gives Access-Control-Allow-Origin: https://myapp.com and Access-Control-Allow-Credentials: true on GET and recognized preflight. No extra authentication, cookies or routes were invented. Real cross-site cookie delivery and third-party-cookie restrictions were not tested.
  5. Node.js service-discovery script's literal local behavior works. The copied script passes syntax checking. Without either endpoint variable it exits with the exact documented guard: “The catalog endpoint is not configured. Run the app through Aspire.” With a controlled CATALOG_HTTP endpoint fixture it requests /, prints catalog-test-response, and exits zero. This does not verify live Aspire injection or a trusted HTTPS catalog endpoint.
  6. Node.js worker and all three complete Express examples pass syntax checking as copied. The worker's Redis connectivity, shutdown handling and actual orchestration were not executed. The browser's copy buttons preserve literal newlines and comments; no copy corruption was reproduced.

CLI Commands — 2 logical checks

  1. dotnet new worker --name DataWorker --output DataWorker passed in an artifact-only directory. The Worker Service template was created and its restore succeeded.
  2. dotnet add package Aspire.StackExchange.Redis passed from the generated DataWorker directory, exactly as displayed in the client install panel. This machine's configured NuGet feeds resolved version 13.6.1; that is an observed environment resolution, not a claim about a required version or a source-code accuracy verdict. The package was compatible with the generated target framework.

Links — 4 logical checks

  1. Both scoped routes load with the expected page titles and PR-specific content.
  2. Actual sidebar navigation console → CORS → console works. Warning 2 concerns changed preference state, not a broken route.
  3. Console anchors for service discovery, creating a worker, viewing console output and connection strings navigate to their corresponding visible sections.
  4. CORS anchors for injected frontend URL, preflight, credentials and dashboard CORS navigate to their corresponding visible sections.

Unrelated links, redirects, general broken-link checks, lint and style were outside scope and were not audited.

Recommendations

  1. Priority fixes: Address the two reproduced choice/comprehension warnings; no critical runtime defect was demonstrated. Preserve the existing correct independence of application/API and AppHost selections.
  2. Documentation gaps: Explain the installation-method/target-file axis and its applicability to TypeScript AppHosts. Explain or separate the cross-page API/worker preference so a mixed-language application can be followed without silently changing the other role.
  3. Product issues: No Aspire runtime bug was established. Shared cross-page preference behavior may warrant a documentation-component/state change, but it should be scoped to the reproduced role coupling rather than a general tab redesign.

Documentation Fix Required — package selection

Page: Both affected routes.
Section: Console hosting-package instructions; CORS frontend injection hosting-package instructions.
Current Content: “The examples use two sets of tabs, which you choose independently,” followed by a third package-format group offering C# files.
Problem: TypeScript AppHost readers are not given the rule for the C# installation choices displayed above their TypeScript code.
Suggested Fix: Name the third axis and specify the applicable install route/target file for each AppHost format.
Verification: Select Node.js + TypeScript; cycle every install option; confirm the reader receives explicit applicable instructions without needing to infer a nonexistent file.

Documentation Fix Required — cross-page language roles

Page: /app-host/console-apps/ and /app-host/cors-configuration/.
Section: Application/API-language selections.
Current Content: One page selects the console app's language, the other the API's language, but both share one persisted preference.
Problem: Configuring a .NET API resets a previously chosen Node.js console/worker procedure.
Suggested Fix: Separate the role preferences, or explicitly surface the shared global preference and reselection behavior.
Verification: Follow Node.js console → .NET API → console with C# or TypeScript AppHost fixed; ensure the combination is retained or its change clearly explained.

Knowledge Gap: Installation choice applicability

What I needed to know: Which installation choice to use for a TypeScript AppHost, versus a C# file-based AppHost or C# project AppHost.

Source of my knowledge: Built-in/prior knowledge (NOT the two-axis explanation) would be needed to confidently decide what to do with a C# package directive or .csproj fragment when the selected AppHost is apphost.mts. That knowledge was not used to paste into an invented file or silently repair an example.

User impact: Especially relevant to Node.js developers who may not already know C# file-based apps, .csproj files or NuGet package-reference formats.

Recommendation:

  • Add explanation to this page.
  • Link to prerequisite documentation as the only remedy — a nearby concrete selection rule is more direct.
  • Reasonable to assume — not for the mixed-language walkthrough that offers these choices.

Knowledge Gap: A selected language is also a cross-page preference

What I needed to know: That choosing API language on the CORS page also changes the console/worker language on another page.

Source of my knowledge: Observed browser behavior and the persisted preference; not the page's description of the distinct roles. No prior implementation knowledge was used.

User impact: Mixed-language readers can return to a procedure that no longer matches their console app and infer that they selected the wrong AppHost or platform.

Recommendation:

  • Add explanation to this page if the shared preference is retained.
  • Link to prerequisite documentation — this is selection behavior, not a prerequisite.
  • Reasonable to assume — the two roles can differ in one application.

Knowledge Gap / coverage limit: Prerequisite applications and live orchestration

What I needed to know: The reader's existing AppHost, service defaults/project references, generated TypeScript modules, Redis/PostgreSQL runtime, frontend script and authentication/telemetry setup.

Source of my knowledge: Explicit prerequisites/context in the rendered pages. Prior knowledge was deliberately not used to construct undocumented substitutes or consult product source.

User impact: These are not fresh-project tutorials. Standalone API/script tests cannot establish that all referenced existing projects and generated AppHost modules work together.

Recommendation:

  • Add explanation to this page — no new missing-prerequisite defect was demonstrated.
  • Link to prerequisite documentation — existing links/context are present; their full procedures were outside this two-page run.
  • Reasonable to assume an existing AppHost here, because it is explicitly stated. Keep the unexecuted outcomes unverified.
Tester route coverage, runtime boundaries, commands and cleanup ownership

Exact route and section coverage

http://127.0.0.1:4321/app-host/console-apps/

  • Adding a console app to the AppHost (console-apps.mdx:13–169): prerequisite/axis explanation; Redis and JavaScript installs; all four app/AppHost combinations; all hosting install formats; package/script examples and connection-value explanation.
  • Service discovery in console apps (171–360): .NET steps and client installation choices; Node.js package/script and endpoint guard; both AppHost examples; lower-group app-language synchronization; literal Node script checks/execution with explicit fixture values.
  • Background worker services / Creating a worker (362–488): .NET command/client-package context; literal dotnet new worker and Redis client installation; Node worker copy/syntax check; placeholder-work explanation.
  • Background worker services / Adding to the AppHost (490–605): PostgreSQL hosting-install choices, both app languages/AppHost languages, unused-database-reference caveat.
  • Explicit start and console interaction (607–699): both app languages/AppHost languages, dashboard start explanation, stdin limitation and surrounding experimental API context; read/rendered, not orchestrated.
  • Viewing console output (701–750): both app-language logging paths; fragment context and exporter requirements; anchor interaction.
  • Using connection strings without service defaults (752–801): .NET provider/installation context and Node exact-name/Redis URI distinction; anchor interaction.

http://127.0.0.1:4321/app-host/cors-configuration/

  • When does CORS matter with Aspire? (cors-configuration.mdx:12–32): browser versus server requests and exact origins; supported by actual same-host/different-origin browser tests.
  • Configuring CORS in development (34–118): .NET and Express branches, prerequisites and fixed-origin caution; exact Express package/server copy, start and browser/HTTP/preflight checks.
  • Using environment-based CORS configuration (120–226): both API languages; Development/Production choices and retention across API changes; exact Node API with no list and both displayed lists.
  • Using Aspire to inject the frontend URL locally (228–411): JavaScript-hosting install choices; frontend dev/PORT and API assumptions; four API/AppHost combinations; development/public-production guidance; exact Node script tested using explicit environment fixtures, not real Aspire injection.
  • Common CORS issues and solutions (413–551): middleware ordering/authentication assumptions, preflight, credentialed requests, cookie caveats, bearer-header distinction and production-origin diagnosis; both language panels read, diagnostic anchors clicked; Express preflight/credential headers and actual browser bearer-header preflight exercised.
  • Dashboard CORS configuration (553–576): explicit origins versus development-only wildcard, AppHost/standalone setting note; anchor/read/rendered only.

The saved pr.diff bounds changed hunks; surrounding existing context was retained for a new reader. Unchanged related routes and product implementation were not inspected to rescue an example.

Matrix evidence

Route App/API AppHost Representative saved evidence
Console .NET C# console-initial.yaml, console-dotnet-csharp-roundtrip.yaml
Console .NET TypeScript console-dotnet-typescript.yaml
Console Node.js TypeScript console-node-typescript.yaml, console-lower-app-toggle-typescript.yaml
Console Node.js C# console-node-csharp.yaml
CORS Node.js C# cors-from-console-node-csharp.yaml, cors-node-csharp-development.yaml
CORS Node.js TypeScript cors-node-typescript.yaml, cors-final-node-typescript.yaml
CORS .NET TypeScript cors-dotnet-typescript.yaml
CORS .NET C# cors-dotnet-csharp-production.yaml

Additional evidence records all install formats, Development/Production panels, the cross-page preference reproduction and anchor navigation. URL aspire-lang changes followed actual AppHost selections; app/API preference is persisted separately and not inferred from that URL parameter.

Runtime evidence and boundaries

Versions observed: Node v24.13.1; Express 5.2.1 and cors 2.8.6, resolved by the literal documented ranges ^5.1.0 / ^2.8.5; .NET SDK 10.0.401; installed Aspire CLI inventory 13.6.0+8230626c0a4656a8417bfebdd61ba455a07b7411. The CLI inventory does not establish package/API compatibility for unexecuted AppHosts.

Literal copies: cors-package-clipboard.txt, cors-development-clipboard.txt, cors-environment-clipboard.txt, cors-injected-clipboard.txt, cors-credentials-clipboard.txt, console-discovery-clipboard.txt, console-worker-clipboard.txt, worker-create-clipboard.txt, worker-redis-install-clipboard.txt.

Reproducible instrumentation: runtime/cors-tests.mjs and runtime/console-discovery-test.mjs are test harnesses, not added documentation instructions. They start the unchanged copied scripts, supply controlled environment values, assert output/headers, and clean up their own specific child processes. The sole code composition replaces an existing CORS policy with the displayed credentials fragment as the page instructs. A minimal local HTML origin and catalog-response fixture are measurement infrastructure, not substitutions for documented Aspire resource setup.

Successful execution evidence:

  • runtime/cors-http-results.json and runtime/cors-http-transcript.txt: 28 assertions, GET and OPTIONS for 14 cases; exit zero; CLEANUP_COMPLETE checks=28.
  • cors-browser-results.txt: at origin http://localhost:3000, fixed Development, allowlisted Development and injected Development return 200/OK; unset, public-only Production, injected Production and missing endpoint/list produce expected browser TypeError: Failed to fetch.
  • cors-browser-bearer-preflight.txt: real browser request with an explicit test Authorization header succeeds with 200/OK, without enabling credentials.
  • cors-browser-unlisted-origin.txt and cors-unlisted-origin-error-evidence.log: http://127.0.0.1:3000 cannot read the fixed http://localhost:3000 allowlist response. The test token is a synthetic value, not a credential.
  • cors-browser-error-evidence.log: expected CORS browser diagnostics for disallowed/absent origins.
  • runtime/console-discovery-transcript.txt: expected missing-endpoint guard and successful HTTP fallback to /.
  • runtime/worker-create-transcript.txt and runtime/worker-redis-install-transcript.txt: both exact .NET commands succeeded.

Harness/environment problems resolved, not documentation findings: The first DOM textContent extraction joined code lines, so it was not used for execution; actual copy buttons were used instead. Initial clipboard-read permission was denied until granted inside the named CLI browser context. The first harness attempt assumed LF separators while the literal clipboard used CRLF; that instrumentation assertion was corrected without modifying the copied example. runtime/cors-harness-first-attempt.txt and cors-browser-before-fixture.txt are historical diagnostic evidence, not final example failures. npm warned about existing machine always-auth configuration but completed installation successfully.

Explicitly unverified:

  • C# or TypeScript AppHost compilation/execution and actual generated-module/project-reference availability.
  • Real Aspire injection of connection variables, NODE_ENV, PORT, service URLs or public endpoints.
  • End-to-end .NET console/API/worker examples with the existing shared service-defaults project.
  • Redis processor/worker operations, PostgreSQL operations, WaitFor, stop signals, explicit dashboard start, terminal interaction and actual OpenTelemetry exports.
  • HTTPS service-discovery fallback with an actual catalog certificate.
  • Authentication fragments requiring existing middleware/services, real cross-site cookie receipt, browser third-party-cookie policies, deployed origins and a real production environment.
  • Dashboard environment-variable behavior and permissive wildcard runtime behavior.
  • Full package installation into an AppHost via the interactive aspire add flow. The generated .NET worker's client-package installation was executed; no AppHost was invented.

These are coverage limitations, not proven defects or additional warnings.

Precise tools and commands

The complete chronological CLI/runtime command ledger is the frozen companion phase-b-rendered-runtime-commands.txt in this artifact directory. It records the actual named browser, element refs, captures, environment fixtures, copy extraction, installation commands, harness execution and cleanup. The .mjs harnesses contain the exact fixture environment values and asserted HTTP requests.

Core executable commands, all in artifact-only directories:

playwright-cli --help | Select-Object -First 5
playwright-cli -s=pr1845-phaseb open http://127.0.0.1:4321/app-host/console-apps/
npm install --no-audit --no-fund
node --check .\runtime\api\development.js
node --check .\runtime\api\environment.js
node --check .\runtime\api\injected.js
node --check .\runtime\api\discovery.js
node --check .\runtime\api\worker.js
node .\runtime\cors-tests.mjs
node .\runtime\console-discovery-test.mjs
dotnet new worker --name DataWorker --output DataWorker
dotnet add package Aspire.StackExchange.Redis

Only functions.powershell, functions.read_powershell, functions.view, functions.apply_patch and parallel batching of independent reads/probes were used during this rerun. Skills were already active from the initial run and not reinvoked. functions.apply_patch only created/corrected artifact harnesses and wrote the requested report/ledger.

Cleanup and ownership

The bounded CORS harness completed normally and stopped its own eight Node child processes and its frontend fixture. The catalog fixture also closed normally. Browser/session scratch, Node dependencies and generated worker-project scratch are removed after evidence preservation. Literal copied text, harnesses, screenshots, snapshots and result transcripts remain as frozen evidence.

The named browser pr1845-phaseb is closed. The review-owned docs preview on port 4321 is not stopped by this tester; parent owns Phase C cleanup. The repository remains unmodified. No GitHub review or comment was posted.

Final observed verification: The runtime result file contains 28 HTTP assertions; the browser result records the real http://localhost:3000 origin; CLI confirmed Browser 'pr1845-phaseb' closed; no listeners remain on the owned fixture ports; the preview remains listening at 127.0.0.1:4321 with PID 26296; git status --short is empty.

Comment thread src/frontend/src/content/docs/app-host/console-apps.mdx
- **.NET** and **Node.js** tabs select the language of the API that the AppHost orchestrates.
- **C#** and **TypeScript** tabs select the language of the AppHost itself.

<Tabs syncKey='console-app-lang'>

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.

This shares the console/worker page's persisted application-language preference, but it selects a different application role here. I reproduced Node.js console + C# AppHost -> CORS page -> choose .NET API -> return to console: the console page now shows .NET instead of the previously selected Node.js worker. The C# AppHost choice remains correct.

A Node.js worker calling a .NET API is a valid mixed-language combination, so changing the API procedure shouldn't silently change the worker procedure. Please use distinct preference groups for API language and console/worker language while retaining same-page synchronization and the shared AppHost-language preference, or clearly explain that this is a global preference requiring reselection. The current role-specific descriptions don't disclose that coupling.

… it to display only the Aspire CLI command on TypeScript tabs.
@IEvangelist
David Pine (IEvangelist) merged commit 216e87d into microsoft:copilot/add-docs-for-app-model-api Oct 13, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants