Repository navigation
Add JavaScript code for non-AppHost examples - #1845
Conversation
David Pine (IEvangelist)
left a comment
There was a problem hiding this comment.
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)... asCATALOG_HTTPSandCATALOG_HTTP" applies to the endpoints actually configured oncatalog. 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 andfinally. The orchestrator can escalate to forceful termination; cleanup is not guaranteed after a forced kill. The loop delays ten seconds afterdoWork, 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
ioredispackage, 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.emitfragment (console-apps.mdx:731-745). - A48: Literal Node
process.envlookup (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_ORIGINSvariable, 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-headconsole-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-headcors-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:
- On console apps, choose Node.js with a C# AppHost.
- Expand AppHost → Configuration and click the actual CORS configuration sidebar link.
- Observe Node.js already selected for the API.
- Change the API selection to .NET while keeping C# AppHost selected.
- 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
- 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.csunder .NET + TypeScript is a .NET application sample, not evidence that the TypeScript AppHost selection is broken. - Console-page four-way matrix works. .NET/C#, .NET/TypeScript, Node.js/TypeScript and Node.js/C# all display the corresponding
AddProject/addProjectorAddNodeApp/addNodeAppblock and the appropriate AppHost filename. All visible app-language groups agree. - 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.
- 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.
- 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.
- 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 displaysappsettings.jsonwith public origins. Returning to Node.js retains Production; selecting Development displays the localhost-origin setting. - All three hosting-install formats are selectable and synchronize consistently. CLI, C# file-based and
.csprojchoices 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. - All three .NET client-package formats are selectable.
.NET CLI,Program.cs (C# file-based app)andPackageReference (*.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. - 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, frontenddevscript/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. - 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
- Complete fixed-origin Express example runs literally. The
api/package.jsonandapi/server.jswere copied through their actual rendered copy buttons, with imports, line breaks and comments intact. The unchanged server starts on its documented default port 5000, returns200 OK, and supplies the specified localhost origin. Allowed preflight returns 204 and includes permitted methods/requested headers. A real browser athttp://localhost:3000reads the response; a browser athttp://127.0.0.1:3000cannot, because that is a different origin. A nonmatching fixed allow-origin header is not incorrectly counted as permission for the requester's origin. - 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. - Complete injected-endpoint Express example runs literally under controlled documented environment values. With
NODE_ENV=developmentand aFRONTEND_HTTPfixture 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. - 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.comandAccess-Control-Allow-Credentials: trueon 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. - 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_HTTPendpoint fixture it requests/, printscatalog-test-response, and exits zero. This does not verify live Aspire injection or a trusted HTTPS catalog endpoint. - 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
dotnet new worker --name DataWorker --output DataWorkerpassed in an artifact-only directory. The Worker Service template was created and its restore succeeded.dotnet add package Aspire.StackExchange.Redispassed from the generatedDataWorkerdirectory, exactly as displayed in the client install panel. This machine's configured NuGet feeds resolved version13.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
- Both scoped routes load with the expected page titles and PR-specific content.
- Actual sidebar navigation console → CORS → console works. Warning 2 concerns changed preference state, not a broken route.
- Console anchors for service discovery, creating a worker, viewing console output and connection strings navigate to their corresponding visible sections.
- 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
- 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.
- 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.
- 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; literaldotnet new workerand 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; frontenddev/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.jsonandruntime/cors-http-transcript.txt: 28 assertions, GET and OPTIONS for 14 cases; exit zero;CLEANUP_COMPLETE checks=28.cors-browser-results.txt: at originhttp://localhost:3000, fixed Development, allowlisted Development and injected Development return 200/OK; unset, public-only Production, injected Production and missing endpoint/list produce expected browserTypeError: 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.txtandcors-unlisted-origin-error-evidence.log:http://127.0.0.1:3000cannot read the fixedhttp://localhost:3000allowlist 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.txtandruntime/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 addflow. 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.RedisOnly 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.
| - **.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'> |
There was a problem hiding this comment.
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.
216e87d
into
microsoft:copilot/add-docs-for-app-model-api
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
pnpm test