Davidfowl/ts apphost#13705
Conversation
- Introduced AppHostRunner and AppHostRunnerFactory for managing different AppHost types. - Implemented validation and execution logic for TypeScript (apphost.ts) and Python (apphost.py) AppHosts. - Enhanced ProjectLocator to detect and handle TypeScript and Python AppHost files. - Added AppHostType enum to represent different AppHost project types.
…selection and execution
- Added ProjectModel class to scaffold and manage the GenericAppHost project. - Introduced Instruction models for handling various commands (Create, Run, Pragma, Declare, Invoke). - Developed InstructionProcessor to execute instructions and manage application builders. - Created JsonRpcServer to handle client connections and execute instructions via JSON-RPC. - Implemented OrphanDetector to monitor the host process and exit if it is no longer running. - Added Program class to initialize and start the JsonRpc server with graceful shutdown handling.
…der with JSON-RPC support
- Added ICodeGenerationService interface for generating TypeScript code from Aspire packages. - Created TypeScriptCodeGenerator class for generating TypeScript SDK from the Aspire application model. - Introduced ApplicationModel, IntegrationModel, ResourceModel, and related classes to represent the application structure. - Developed client and distributed application builder for managing resources and executing instructions in TypeScript. - Added TypeScript project files and dependencies for the new code generation functionality.
…ronment variable handling
… and Redis SDK generation
- Added IAppHostProject interface for defining common methods for AppHost projects. - Created AddPackageContext class to encapsulate package addition context. - Implemented PythonAppHostProject class to handle Python AppHost projects, including validation, running, and package management. - Implemented TypeScriptAppHostProject class to handle TypeScript AppHost projects, including validation, running, and package management. - Introduced IAppHostProjectFactory interface for creating AppHost project handlers based on type. - Added methods for managing dependencies and environment setup for both Python and TypeScript projects.
…nused dependencies
…ce and implement TypeScriptCodeGenerator
…eval based on local package source
… JsonRpcServer for graceful shutdown
… support and thread safety
…and enhance environment variable handling
… for polyglot SDKs
… improve environment variable handling
- Deleted client TypeScript definitions and implementations, including callback registration and remote app host client logic. - Removed distributed application builder and resource builder classes, along with their associated methods and types. - Eliminated launch settings reader and related functions for managing application launch configurations. - Cleaned up index files by removing exports related to the deleted modules. - Removed package.json and package-lock.json files as they are no longer needed.
…nteractions in TypeScript
…and error propagation; add ListProxy for .NET collections
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 13705Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 13705" |
… TypeScript and .NET interactions
… proxy handling; update InstructionProcessor for marshalled results
…eration based on ASPIRE_REPO_ROOT configuration
- Removed Python AppHost project handling from AppHostProjectFactory and related classes. - Deleted PythonAppHostProject.cs and its associated logic for validation and execution. - Updated AppHostLanguage and AppHostType enums to exclude Python. - Modified InitCommand and NewCommand to no longer create Python AppHost templates. - Adjusted RunCommand and ProjectLocator to eliminate Python-specific checks. - Cleaned up comments and documentation to reflect the removal of Python support.
- Added RemoteAppHostService to handle JSON-RPC methods for executing instructions, invoking methods, and managing properties. - Introduced JsonRpcServer to manage client connections over a Unix domain socket, including graceful shutdown and client disconnection handling. - Implemented OrphanDetector as a background service to monitor the parent process and trigger shutdown if it dies. - Integrated orphan detection into the server startup process, allowing for automatic shutdown on parent process termination. - Enhanced error handling and logging throughout the server and service methods.
…instruction processing
- Implement JsonRpcCallbackInvoker to handle callback invocations using JSON-RPC. - Create ObjectRegistry to manage registration and lookup of .NET objects for marshalling to/from TypeScript. - Update RemoteAppHostService to utilize the new JsonRpcCallbackInvoker. - Add unit tests for InstructionProcessor and ObjectRegistry to ensure functionality. - Implement TestCallbackInvoker for testing callback invocations.
…mance and features
- Added ReferenceExpression class for creating and serializing reference expressions. - Introduced ResourceBuilderBase, AspireList, and AspireDict classes for resource management. - Developed transport layer with Handle, CapabilityError, and callback registration. - Implemented AspireClient for JSON-RPC connection handling and capability invocation. - Added error handling for ATS capabilities and structured error responses.
…ement - Added methods to scan assemblies and return an AtsContext for code generation. - Updated ICodeGenerator interface to generate code from AtsContext instead of separate capabilities and DTO types. - Introduced AtsEnumTypeInfo to represent enum types discovered during scanning. - Enhanced AtsCapabilityScanner to collect and manage enum types during assembly scanning. - Updated AtsMarshaller to handle unmarshalling of enums from string and numeric values. - Added tests for enum handling in marshalling and capability dispatching. - Updated TypeScript code generation to include generated enums and their usage in DTOs and options.
… update related documentation
…ndling and argument serialization details
…ntime stability and API evolution
| @@ -0,0 +1 @@ | |||
| BB3E5A59ABF66ED257D929D56559C54898462D19316B5AE7E86B7011FF370555 No newline at end of file | |||
There was a problem hiding this comment.
Is this folder supposed to be tracked? The cli is still required to start the apphost so it would still be re-generated if not.
There was a problem hiding this comment.
Reasons to keep it: detect changes when updating codegen, see what it looks like (same as manifest files)
| - Aspire.Hosting.Redis/withDataVolume | ||
| - ThirdParty.Integration/withDataVolume | ||
|
|
||
| Resolution: Use [AspireExport("uniqueMethodName")] to disambiguate. |
There was a problem hiding this comment.
Is it really a resolution or a requirement on the library authors? What about something that the app host dev could specify in an options file, could even rename anything even if there aren't any conflicts.
There was a problem hiding this comment.
Then next level of option is to be able to aspire-import any method that might not be exported by default. (kind of private reflection).
|
|
||
| **Startup Sequence:** | ||
|
|
||
| 1. CLI scaffolds an AppHost server project in `$TMPDIR/.aspire/hosts/<hash>/` |
There was a problem hiding this comment.
... unique to the apphost project location
| 2. CLI adds required hosting packages (Redis, Postgres, etc.) | ||
| 3. CLI builds the .NET project | ||
| 4. Code generation scans assemblies for `[AspireExport]` and generates SDK | ||
| 5. CLI starts the AppHost server with socket path |
There was a problem hiding this comment.
Is guest runtime the official name? No "App Host" something
There was a problem hiding this comment.
Yes apphost sever and guest
|
|
||
| | Environment Variable | Description | Example | | ||
| |---------------------|-------------|---------| | ||
| | `REMOTE_APP_HOST_SOCKET_PATH` | Unix socket path (or named pipe name on Windows) | `/tmp/aspire/host.sock` | |
There was a problem hiding this comment.
Should it be renamed to match the name "App Host Server" instead of "remote"?
| CLI->>Guest: Start (socket path via env var) | ||
|
|
||
| Guest->>Host: invokeCapability("Aspire.Hosting/createBuilder", {}) | ||
| Host-->>Guest: { $handle: "1", $type: "Aspire.Hosting/Aspire.Hosting.IDistributedApplicationBuilder" } |
There was a problem hiding this comment.
Will check. Are handles strings (unique)?
There was a problem hiding this comment.
They are ints today but will probably need to change that (overflow), the handle is is unique.
|
|
||
| ## Aspire Type System (ATS) | ||
|
|
||
| ATS is the central type system that bridges .NET and guest languages. Every type crossing the boundary has an **ATS type ID** that serves as its portable identity. |
There was a problem hiding this comment.
Should guest runtimes have hooks/events/filters to be able to alter the messages?
There was a problem hiding this comment.
When we need it we can add it. What scenario are you thinking ?
|
|
||
| ### Type Exporting and Polymorphism Flattening | ||
|
|
||
| ATS doesn't have a closed set of primitive types. Instead, any .NET type can be exported using `[AspireExport]`, and the scanner automatically expands capabilities based on type relationships. |
There was a problem hiding this comment.
Do we have a command to list the capabilities of a specific project? Might be useful for API reviews and detect breaking changes. Or even debugging.
There was a problem hiding this comment.
I’m not sure that’s useful for anyone but us so not a command but something we can do in debug mode
| Declared on extension methods: | ||
|
|
||
| ```csharp | ||
| [AspireExport("addRedis", Description = "Adds a Redis resource")] |
There was a problem hiding this comment.
Do we need a doc format for things like see cref but using ATS? Are parameters documented?
|
|
||
| | Direction | `[AspireDto]` Type | Non-`[AspireDto]` Type | | ||
| |-----------|-------------------|----------------------| | ||
| | Input (JSON → .NET) | Deserialized | **Error** | |
There was a problem hiding this comment.
Is it really an Error or more precisely it should be a handle that is sent (otherwise error)
|
Looks really cool, is this a similar pattern to how playwright supports multiple languages? |
I didn't look at playwright. @sebastienros and I built several prototypes over the last year, and this is the approach we liked best. |
FYI @pavelfeldman |
|
@Meir017 your comment got me curious so I investigated as I had never actually looked at how playwright works.
Key differences:
Disclaimer: based on my (and ChatGPT's) understanding of playwright |
Polyglot AppHost Support: Write Aspire App Hosts in TypeScript
Overview
This PR introduces polyglot AppHost support, enabling developers to write Aspire app hosts in non-.NET languages. TypeScript is the first supported language, with the architecture designed to support additional languages (Go, Python, etc.) in the future.
This work builds on the foundational JSON-RPC infrastructure established by @sebastienros in #11667.
What's New
Developers can now create and run Aspire app hosts entirely in TypeScript:
Aspire Type System (ATS)
The core innovation is the Aspire Type System (ATS) - a portable type system that maps .NET types to a unified representation any language can work with:
{Assembly}/{FullTypeName}Aspire.Hosting.Redis/addRedisIntegration authors expose their existing extension methods by adding
[AspireExport]attributes - no wrapper code needed:Architecture
The CLI orchestrates two processes that communicate via JSON-RPC over Unix domain sockets:
flowchart TB subgraph CLI["Aspire CLI"] direction LR subgraph Guest["Guest Runtime (Node.js)"] direction TB UserCode["User Code<br/>(apphost.ts)"] SDK["Generated SDK<br/>(aspire.ts)"] ATSClient["ATS Client"] UserCode --> SDK --> ATSClient end subgraph Host["AppHost Server (.NET)"] direction TB Packages["Aspire.Hosting.*<br/>(Redis, etc)"] Dispatcher["Capability Dispatcher"] RPCServer["JSON-RPC Server"] Packages --> Dispatcher --> RPCServer end ATSClient <-->|"JSON-RPC<br/>(Unix Socket)"| RPCServer end CLI -.->|spawns| Guest CLI -.->|spawns| HostKey Components
Capability Scanner (
Aspire.Hosting)[AspireExport]attributesCode Generation (
Aspire.Hosting.CodeGeneration.TypeScript)RemoteHost (
Aspire.Hosting.RemoteHost)CLI Integration (
Aspire.Cli)TypeScriptAppHostProjectimplementsIAppHostProjectFeature Flag
Polyglot support is behind a feature flag during preview. Enable it globally:
CLI Commands
Once the feature flag is enabled, the
--languageoption becomes available:aspire init --language typescriptaspire new --language typescriptaspire runaspire publishaspire add <package>Type System Design
ATS flattens .NET's polymorphism into a simple, portable model:
Each concrete type gets a complete list of capabilities - no type hierarchy to reason about in guest languages.
Generated TypeScript Features
await builder.addRedis("cache").withRedisCommander()refExpr`redis://${endpoint}`withEnvironmentCallback(async (ctx) => { ... })AspireDict<K,V>andAspireList<T>for mutable .NET collectionsSecurity
Both guest and host run locally, started by the CLI:
[AspireExport]methods callable)[AspireDto]types serialized)Documentation
See docs/specs/polyglot-apphost.md for complete specification including:
Testing